diff --git a/.jshintrc b/.jshintrc index c1f2978b..a8e1e5f9 100644 --- a/.jshintrc +++ b/.jshintrc @@ -1,3 +1,10 @@ { - "node": true + "node": true, + "globals": { + "describe" : true, + "it" : true, + "Node" : true, + "suite" : true, + "test" : true + } } diff --git a/.travis.yml b/.travis.yml index b02f3658..07becfb2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,11 +1,11 @@ language: node_js node_js: - - "0.4" - - "0.6" - - "0.8" + - "5.0" + - "4.2" - "0.10" - "0.11" matrix: allow_failures: - - node_js: "0.11" + - node_js: "0.10" + - node_js: "0.11" \ No newline at end of file diff --git a/Makefile b/Makefile index 6cde1304..4d56d98a 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,17 @@ -.PHONY: jshint test - -jshint: - ./node_modules/.bin/jshint lib test +.PHONY: jshint test publish-patch test test: npm test + +patch: test + npm version patch -m "Bump version" + git push origin master --tags + npm publish + +minor: test + npm version minor -m "Bump version" + git push origin master --tags + npm publish + +jshint: + ./node_modules/.bin/jshint lib diff --git a/README.md b/README.md index 0cfb115b..e1a3af8c 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # node-sql -_sql string builder for node_ - supports PostgreSQL, mysql, and sqlite dialects. +_sql string builder for node_ - supports PostgreSQL, mysql, Microsoft SQL Server, Oracle and sqlite dialects. Building SQL statements by hand is no fun, especially in a language which has clumsy support for multi-line strings. @@ -9,12 +9,22 @@ Maybe it's still not fun, but at least it's _less not fun_. [![Build Status](https://secure.travis-ci.org/brianc/node-sql.png)](http://travis-ci.org/brianc/node-sql) -## examples +## install + +```sh +$ npm install sql +``` + +## use ```js //require the module var sql = require('sql'); +//(optionally) set the SQL dialect +sql.setDialect('postgres'); +//possible dialects: mssql, mysql, postgres (default), sqlite + //first we define our tables var user = sql.define({ name: 'user', @@ -45,6 +55,9 @@ console.log(query.text); //SELECT "user"."id" FROM "user" WHERE ((("user"."name" console.log(query.values); //['boom', 1, 'bang', 2] +//queries can be named +var query = user.select(user.star()).from(user).toNamedQuery('user.all'); +console.log(query.name); //'user.all' //how about a join? var query = user.select(user.name, post.body) @@ -95,7 +108,7 @@ console.log(user.select().where(user.state.equals('WA')).toQuery().text); // "SELECT "user".* FROM "user" WHERE ("user"."state_or_province" = $1)" ``` -There are a __lot__ more examples included in the `test/dialects` folder. +There are a __lot__ more examples included in the [test/dialects](https://github.com/brianc/node-sql/tree/master/test/dialects) folder. We encourage you to read through them if you have any questions on usage! ## from the command line You can use the [sql-generate module](https://github.com/tmont/node-sql-generate) @@ -147,7 +160,7 @@ Read the module's documentation for more details. We __love__ contributions. -node-sql wouldn't be anything without all the contributors and collaborators who've worked on it. +node-sql wouldn't be anything without all the contributors and collaborators who've worked on it. If you'd like to become a collaborator here's how it's done: 1. fork the repo @@ -156,7 +169,7 @@ If you'd like to become a collaborator here's how it's done: 4. `npm install` 5. `npm test` -At this point the tests should pass for you. If they don't pass please open an issue with the output or you can even send me an email directly. +At this point the tests should pass for you. If they don't pass please open an issue with the output or you can even send me an email directly. My email address is on my github profile and also on every commit I contributed in the repo. Once the tests are passing, modify as you see fit. _Please_ make sure you write tests to cover your modifications. Once you're ready, commit your changes and submit a pull request. @@ -165,10 +178,10 @@ __As long as your pull request doesn't have completely off-the-wall changes and If you think your changes are too off-the-wall, open an issue or a pull-request without code so we can discuss them before you begin. -Usually after a few high-quality pull requests and friendly interactions we will gladly share collaboration rights with you. +Usually after a few high-quality pull requests and friendly interactions we will gladly share collaboration rights with you. After all, open source belongs to everyone. -##license +## license MIT diff --git a/lib/column.js b/lib/column.js index 8f835b42..55a3da18 100644 --- a/lib/column.js +++ b/lib/column.js @@ -2,22 +2,25 @@ var _ = require('lodash'); var ColumnNode = require('./node/column'); -var OrderByColumnNode = require('./node/orderByColumn'); +var OrderByValueNode = require('./node/orderByValue'); var TextNode = require('./node/text'); var valueExpressionMixin = require('./node/valueExpression'); var Column = function(config) { this.table = config.table; - for(var name in config) { - this[name] = config[name]; + for (var name in config) { + if (config.hasOwnProperty(name)) { + this[name] = config[name]; + } } this.asc = this.ascending = this; this.alias = null; - this.desc = this.descending = new OrderByColumnNode({ - column: this.toNode(), - direction: new TextNode('DESC') + this.desc = this.descending = new OrderByValueNode({ + value : this.toNode(), + direction : new TextNode('DESC') }); this.dataType = config.dataType; + this.defaultValue = config.defaultValue; }; // mix in value expression @@ -32,8 +35,9 @@ var contextify = function(base) { }; Column.prototype.value = function(value) { - this._value = value; - return this; + var context = contextify(this); + context._value = value; + return context; }; Column.prototype.getValue = function() { diff --git a/lib/dialect/index.js b/lib/dialect/index.js index e04a1b4e..c26f15de 100644 --- a/lib/dialect/index.js +++ b/lib/dialect/index.js @@ -1,3 +1,5 @@ +'use strict'; + // given a dialect name, return the class var getDialect = function(dialect) { switch (dialect.toLowerCase()) { @@ -7,6 +9,10 @@ var getDialect = function(dialect) { return require('./mysql'); case 'sqlite': return require('./sqlite'); + case 'mssql': + return require('./mssql'); + case 'oracle': + return require('./oracle'); default: throw new Error(dialect + ' is unsupported'); } diff --git a/lib/dialect/mssql.js b/lib/dialect/mssql.js new file mode 100644 index 00000000..6e71f2e1 --- /dev/null +++ b/lib/dialect/mssql.js @@ -0,0 +1,481 @@ +// TODO: visitCreate needs to support schemas +// TODO: visitDrop needs to support schemas + +'use strict'; + +var util = require('util'); +var assert = require('assert'); + +/** + * Config can contain: + * + * questionMarkParameterPlaceholder:true which will use a "?" for the parameter placeholder instead of the @index. + * + * @param config + * @constructor + */ +var Mssql = function(config) { + this.output = []; + this.params = []; + this.config = config || {}; +}; + +var Postgres = require('./postgres'); + +util.inherits(Mssql, Postgres); + +Mssql.prototype._myClass = Mssql; + +Mssql.prototype._quoteCharacter = '['; + +Mssql.prototype._arrayAggFunctionName = ''; + +Mssql.prototype.visitReplace = function(replace) { + throw new Error('Mssql does not support REPLACE.'); +}; + +Mssql.prototype._getParameterPlaceholder = function(index, value) { + if (this.config.questionMarkParameterPlaceholder) return '?'; + return '@' + index; +}; + +Mssql.prototype.visitBinary = function(binary) { + if(binary.operator === '@@'){ + var self = this; + var text = '(CONTAINS (' + this.visit(binary.left) + ', '; + text += this.visit(binary.right); + text += '))'; + return [text]; + } + + if(binary.operator === '||'){ + return ['(' + this.visit(binary.left) + ' + ' + this.visit(binary.right) + ')']; + } + + if (!isRightSideArray(binary)){ + return Mssql.super_.prototype.visitBinary.call(this, binary); + } + if (binary.operator=='IN' || binary.operator=='NOT IN'){ + return Mssql.super_.prototype.visitBinary.call(this, binary); + } + throw new Error('SQL Sever does not support arrays in this type of expression.'); +}; + +Mssql.prototype.visitAlter = function(alter) { + var self=this; + var errMsg='ALTER TABLE cannot be used to perform multiple different operations in the same statement.'; + + // Implement our own add column: + // PostgreSQL: ALTER TABLE "name" ADD COLUMN "col1", ADD COLUMN "col2" + // Mssql: ALTER TABLE [name] ADD [col1], [col2] + function _addColumn(){ + self._visitingAlter = true; + var table = self._queryNode.table; + self._visitingAddColumn = true; + var result='ALTER TABLE '+self.visit(table.toNode())+' ADD '+self.visit(alter.nodes[0].nodes[0]); + for (var i= 1,len=alter.nodes.length; i= 0) txt = _extract(); + // Override CURRENT_TIMESTAMP function to remove parens + else if ('CURRENT_TIMESTAMP' == functionCall.name) txt = functionCall.name; + else { + var name = functionCall.name; + // override the LENGTH function since mssql calls it LEN + if (name == "LENGTH") name = "LEN"; + txt = name + '(' + functionCall.nodes.map(this.visit.bind(this)).join(', ') + ')'; + } + this._visitingFunctionCall = false; + return [txt]; +}; + +Mssql.prototype.visitJoin = function(join) { + if (join.subType !== 'LEFT LATERAL') return Mssql.super_.prototype.visitJoin.call(this, join); + var result = []; + this._visitingJoin = true; + result = result.concat(this.visit(join.from)); + result = result.concat('OUTER APPLY'); + result = result.concat(this.visit(join.to)); + return result; +}; + +Mssql.prototype.visitOrderBy = function(orderBy) { + var result=Mssql.super_.prototype.visitOrderBy.call(this, orderBy); + var offsetNode=orderBy.msSQLOffsetNode; + var limitNode=orderBy.msSQLLimitNode; + if (!offsetNode && !limitNode) return result; + assert(offsetNode,"Something bad happened, should have had an msSQLOffsetNode here."); + result.push("OFFSET "+getModifierValue(this,offsetNode)+" ROWS"); + if (!limitNode) return result; + result.push("FETCH NEXT "+getModifierValue(this,limitNode)+" ROWS ONLY"); + return result; +}; + +/** + * We override this so that we can deal with the LIMIT and OFFSET clauses specially since they have to become + * part of the SELECT and ORDER BY clauses. + * + * Basically if there's an ORDER BY clause we attach OFFSET and LIMIT to it so that it can be processed by the + * ORDER BY handler later. + * + * If there's a LIMIT clause without OFFSET, we attach it to the SELECT clause so we can process it later. + * + * @param {Node[]} actions + * @param {Node[]} targets + * @param {Node[]} filters + * @returns {String[]} + */ +Mssql.prototype.visitQueryHelper=function(actions,targets,filters){ + function _handleLimitAndOffset(){ + var limitInfo=Mssql.super_.prototype.findNode.call(this, filters, "LIMIT"); // jshint ignore:line + var offsetInfo=Mssql.super_.prototype.findNode.call(this, filters, "OFFSET"); // jshint ignore:line + var orderByInfo=Mssql.super_.prototype.findNode.call(this, filters, "ORDER BY"); // jshint ignore:line + + // no OFFSET or LIMIT then there's nothing special to do + if (!offsetInfo && !limitInfo) return; + // ORDER BY with OFFSET we have work to do, may consume LIMIT as well + if (orderByInfo && offsetInfo) _processOrderByOffsetLimit(orderByInfo,offsetInfo,limitInfo); + else if (offsetInfo) throw new Error("MS SQL Server does not allow OFFSET without ORDER BY"); + else if (limitInfo) _processLimit(limitInfo); + } + + /** + * We need to turn LIMIT into a TOP clause on the SELECT STATEMENT + * + * @param limitInfo + * @private + */ + function _processLimit(limitInfo){ + var selectInfo=Mssql.super_.prototype.findNode.call(this, actions, "SELECT"); // jshint ignore:line + assert(selectInfo!==undefined,"MS SQL Server requires a SELECT clause when using LIMIT"); + // save the LIMIT node with the SELECT node + selectInfo.node.msSQLLimitNode=limitInfo.node; + // remove the LIMIT node from the filters so it doesn't get processed later. + filters.splice(limitInfo.index,1); + } + + /** + * We need to turn LIMIT into a TOP clause on the SELECT STATEMENT + * + * @param orderByInfo + * @param offsetInfo + * @param limitInfo + * @private + */ + function _processOrderByOffsetLimit(orderByInfo,offsetInfo,limitInfo){ + // save the OFFSET AND LIMIT nodes with the ORDER BY node + orderByInfo.node.msSQLOffsetNode=offsetInfo.node; + if (limitInfo) orderByInfo.node.msSQLLimitNode=limitInfo.node; + // remove the OFFSET and LIMIT nodes from the filters so they don't get processed later. + filters.splice(offsetInfo.index,1); + if (limitInfo) filters.splice(limitInfo.index,1); + } + + // MAIN + + Mssql.super_.prototype.handleDistinct.call(this, actions, filters); + _handleLimitAndOffset(); + + // lazy-man sorting + var sortedNodes = actions.concat(targets).concat(filters); + for(var i = 0; i < sortedNodes.length; i++) { + var res = this.visit(sortedNodes[i]); + this.output = this.output.concat(res); + } + return this.output; +}; + +//Mysql.prototype.visitRenameColumn = function(renameColumn) { +// var dataType = renameColumn.nodes[1].dataType || renameColumn.nodes[0].dataType; +// assert(dataType, 'dataType missing for column ' + (renameColumn.nodes[1].name || renameColumn.nodes[0].name || '') + +// ' (CHANGE COLUMN statements require a dataType)'); +// return ['CHANGE COLUMN ' + this.visit(renameColumn.nodes[0]) + ' ' + this.visit(renameColumn.nodes[1]) + ' ' + dataType]; +//}; +// +//Mysql.prototype.visitInsert = function(insert) { +// var result = Postgres.prototype.visitInsert.call(this, insert); +// if (result[2] === 'DEFAULT VALUES') { +// result[2] = '() VALUES ()'; +// } +// return result; +//}; +// +//Mysql.prototype.visitIndexes = function(node) { +// var tableName = this.visit(this._queryNode.table.toNode()); +// +// return "SHOW INDEX FROM " + tableName; +//}; + +Mssql.prototype.visitOnDuplicate = function(onDuplicate) { + throw new Error('MSSQL does not allow onDuplicate clause.'); +}; + +Mssql.prototype.visitOnConflict = function(onConflict) { + throw new Error('MSSQL does not allow onConflict clause.'); +}; + +Mssql.prototype.visitReturning = function() { + // TODO: need to add some code to the INSERT clause to support this since its the equivalent of the OUTPUT clause + // in MS SQL which appears before the values, not at the end of the statement. + throw new Error('Returning clause is not yet supported for MS SQL.'); +}; + +// We deal with SELECT specially so we can add the TOP clause if needed +Mssql.prototype.visitSelect = function(select) { + if (!select.msSQLLimitNode) return Mssql.super_.prototype.visitSelect.call(this, select); + var result=[ + 'SELECT', + 'TOP('+getModifierValue(this,select.msSQLLimitNode)+')', + select.nodes.map(this.visit.bind(this)).join(', ') + ]; + this._selectOrDeleteEndIndex = this.output.length + result.length; + return result; +}; + +// Node is either an OFFSET or LIMIT node +function getModifierValue(dialect,node){ + return node.count.type ? dialect.visit(node.count) : node.count; +} + +function isAlterAddColumn(alter){ + if (alter.nodes.length===0) return false; + if (alter.nodes[0].type!='ADD COLUMN') return false; + return true; +} + +function isAlterDropColumn(alter){ + if (alter.nodes.length===0) return false; + if (alter.nodes[0].type!='DROP COLUMN') return false; + return true; +} + +function isAlterRename(alter){ + if (alter.nodes.length===0) return false; + if (alter.nodes[0].type!='RENAME') return false; + return true; +} + +function isAlterRenameColumn(alter){ + if (alter.nodes.length===0) return false; + if (alter.nodes[0].type!='RENAME COLUMN') return false; + return true; +} + +function isCountStarExpression(columnNode){ + if (!columnNode.aggregator) return false; + if (columnNode.aggregator.toLowerCase()!='count') return false; + if (!columnNode.star) return false; + return true; +} + +function isCreateIfNotExists(create){ + if (create.nodes.length===0) return false; + if (create.nodes[0].type!='IF NOT EXISTS') return false; + return true; +} + +function isCreateTemporary(create){ + return create.options.isTemporary; +} + +function isDropIfExists(drop){ + if (drop.nodes.length===0) return false; + if (drop.nodes[0].type!='IF EXISTS') return false; + return true; +} + +// SQL Server does not support array expressions except in the IN clause. +function isRightSideArray(binary){ + return Array.isArray(binary.right); +} + +module.exports = Mssql; diff --git a/lib/dialect/mysql.js b/lib/dialect/mysql.js index 4765fa91..0a63f3be 100644 --- a/lib/dialect/mysql.js +++ b/lib/dialect/mysql.js @@ -2,13 +2,15 @@ var util = require('util'); var assert = require('assert'); +var _ = require('lodash'); -var Mysql = function() { +var Mysql = function(config) { this.output = []; this.params = []; + this.config = config || {}; }; -var Postgres = require(__dirname + '/postgres'); +var Postgres = require('./postgres'); util.inherits(Mysql, Postgres); @@ -18,11 +20,81 @@ Mysql.prototype._quoteCharacter = '`'; Mysql.prototype._arrayAggFunctionName = 'GROUP_CONCAT'; -/* jshint unused: false */ -Mysql.prototype._getParameterPlaceholder = function(index, value) { +Mysql.prototype.visitReplace = function(replace) { + var self = this; + // don't use table.column for replaces + this._visitedReplace = true; + + var result = ['REPLACE']; + result = result.concat(replace.nodes.map(this.visit.bind(this))); + result.push('INTO ' + this.visit(this._queryNode.table.toNode())); + result.push('(' + replace.columns.map(this.visit.bind(this)).join(', ') + ')'); + + var paramNodes = replace.getParameters(); + + if (paramNodes.length > 0) { + var paramText = paramNodes.map(function (paramSet) { + return paramSet.map(function (param) { + return self.visit(param); + }).join(', '); + }).map(function (param) { + return '('+param+')'; + }).join(', '); + + result.push('VALUES', paramText); + + if (result.slice(2, 5).join(' ') === '() VALUES ()') { + result.splice(2, 3, 'DEFAULT VALUES'); + } + } + + this._visitedReplace = false; + + if (result[2] === 'DEFAULT VALUES') { + result[2] = '() VALUES ()'; + } + return result; +}; + +Mysql.prototype._getParameterPlaceholder = function() { return '?'; }; +Mysql.prototype._getParameterValue = function(value) { + if (Buffer.isBuffer(value)) { + value = 'x' + this._getParameterValue(value.toString('hex')); + } else { + value = Postgres.prototype._getParameterValue.call(this, value); + } + return value; +}; + +Mysql.prototype.visitOnDuplicate = function(onDuplicate) { + var params = []; + /* jshint boss: true */ + for(var i = 0, node; node = onDuplicate.nodes[i]; i++) { + var target_col = this.visit(node); + params = params.concat(target_col + ' = ' + this.visit(node.value)); + } + var result = [ + 'ON DUPLICATE KEY UPDATE', + params.join(', ') + ]; + return result; +}; + +Mysql.prototype.visitOnConflict = function(onConflict) { + throw new Error('Mysql does not allow onConflict clause.'); +}; + +Mysql.prototype.visitReturning = function() { + throw new Error('MySQL does not allow returning clause.'); +}; + +Mysql.prototype.visitForShare = function() { + throw new Error('MySQL does not allow FOR SHARE clause.'); +}; + Mysql.prototype.visitCreate = function(create) { var result = Mysql.super_.prototype.visitCreate.call(this, create); var engine = this._queryNode.table._initialConfig.engine; @@ -48,14 +120,102 @@ Mysql.prototype.visitRenameColumn = function(renameColumn) { Mysql.prototype.visitInsert = function(insert) { var result = Postgres.prototype.visitInsert.call(this, insert); - if (result[2] === 'DEFAULT VALUES') result[2] = '() VALUES ()'; + if (result[2] === 'DEFAULT VALUES') { + result[2] = '() VALUES ()'; + } return result; }; Mysql.prototype.visitIndexes = function(node) { - var tableName = this.visit(this._queryNode.table.toNode()); + var tableName = this.visit(this._queryNode.table.toNode())[0]; return "SHOW INDEX FROM " + tableName; }; +Mysql.prototype.visitBinary = function(binary) { + if (binary.operator === '@@') { + var self = this; + var text = '(MATCH ' + this.visit(binary.left) + ' AGAINST '; + text += this.visit(binary.right); + text += ')'; + return [text]; + } + return Mysql.super_.prototype.visitBinary.call(this, binary); +}; + +Mysql.prototype.visitFunctionCall = function(functionCall) { + var _this=this; + + this._visitingFunctionCall = true; + + function _extract() { + var nodes = functionCall.nodes.map(_this.visit.bind(_this)); + if (nodes.length != 1) throw new Error('Not enough parameters passed to ' + functionCall.name + ' function'); + var txt = functionCall.name + '(' + (nodes[0]+'') + ')'; + return txt; + } + + var txt=""; + var name = functionCall.name; + // Override date functions since mysql is different than postgres + if (['YEAR', 'MONTH', 'DAY', 'HOUR'].indexOf(functionCall.name) >= 0) txt = _extract(); + // Override CURRENT_TIMESTAMP function to remove parens + else if ('CURRENT_TIMESTAMP' == functionCall.name) txt = functionCall.name; + else txt = name + '(' + functionCall.nodes.map(this.visit.bind(this)).join(', ') + ')'; + + this._visitingFunctionCall = false; + return [txt]; +}; + +Mysql.prototype.visitColumn = function(columnNode) { + var self = this; + var inSelectClause; + + function isCountStarExpression(columnNode){ + if (!columnNode.aggregator) return false; + if (columnNode.aggregator.toLowerCase()!='count') return false; + if (!columnNode.star) return false; + return true; + } + + function _countStar(){ + // Implement our own since count(table.*) is invalid in Mysql + var result='COUNT(*)'; + if(inSelectClause && columnNode.alias) { + result += ' AS ' + self.quote(columnNode.alias); + } + return result; + } + + inSelectClause = !this._selectOrDeleteEndIndex; + if(isCountStarExpression(columnNode)) return _countStar(); + return Mysql.super_.prototype.visitColumn.call(this, columnNode); +}; + +Mysql.prototype.visitInterval = function(interval) { + var parameter; + if(_.isNumber(interval.years)) { + if(_.isNumber(interval.months)) { + parameter = "'" + interval.years + '-' + interval.months + "' YEAR_MONTH"; + } else { + parameter = interval.years + ' YEAR'; + } + } else if(_.isNumber(interval.months)) { + parameter = interval.months + ' MONTH'; + } else if(_.isNumber(interval.days)) { + parameter = "'" + interval.days + ' ' + + (_.isNumber(interval.hours)?interval.hours:0) + ':' + + (_.isNumber(interval.minutes)?interval.minutes:0) + ':' + + (_.isNumber(interval.seconds)?interval.seconds:0) + "' DAY_SECOND"; + } else { + parameter = "'" + (_.isNumber(interval.hours)?interval.hours:0) + ':' + + (_.isNumber(interval.minutes)?interval.minutes:0) + ':' + + (_.isNumber(interval.seconds)?interval.seconds:0) + "' HOUR_SECOND"; + } + var result = "INTERVAL " + parameter; + return result; +}; + + + module.exports = Mysql; diff --git a/lib/dialect/oracle.js b/lib/dialect/oracle.js new file mode 100644 index 00000000..a55e7aca --- /dev/null +++ b/lib/dialect/oracle.js @@ -0,0 +1,330 @@ +'use strict'; + +var util = require('util'); +var assert = require('assert'); + +var Oracle = function(config) { + this.output = []; + this.params = []; + this.config = config || {}; +}; + +var Postgres = require('./postgres'); + +var Mssql = require('./mssql'); + +util.inherits(Oracle, Postgres); + +Oracle.prototype._myClass = Oracle; + +Oracle.prototype.visitReplace = function(replace) { + throw new Error('Oracle does not support REPLACE.'); +}; + +Oracle.prototype._aliasText = ' '; +Oracle.prototype._getParameterPlaceholder = function(index, value) { + /* jshint unused: false */ + return ':' + index; +}; + +Oracle.prototype.visitAlias = function(alias) { + var result = [this.visit(alias.value) + ' ' + this.quote(alias.alias)]; + return result; +}; + +Oracle.prototype.visitAlter = function(alter) { + var self=this; + var errMsg='ALTER TABLE cannot be used to perform multiple different operations in the same statement.'; + + // Implement our own add column: + // PostgreSQL: ALTER TABLE "name" ADD COLUMN "col1", ADD COLUMN "col2" + // Oracle: ALTER TABLE "name" ADD ("col1", "col2") + function _addColumn(){ + self._visitingAlter = true; + var table = self._queryNode.table; + self._visitingAddColumn = true; + var result='ALTER TABLE '+self.visit(table.toNode())+' ADD ('+self.visit(alter.nodes[0].nodes[0]); + for (var i= 1,len=alter.nodes.length; i= 0) { + ret.push('ROWS'); + } + if (ret.indexOf('LIMIT') >= 0) { + ret[0] = 'FETCH NEXT'; + ret.push('ROWS ONLY'); + } + return ret; +}; + +Oracle.prototype.visitQueryHelper=function(actions,targets,filters){ + var output = Oracle.super_.prototype.visitQueryHelper.call(this,actions,targets,filters); + + //In Oracle, OFFSET must come before FETCH NEXT (limit) + //Change positions, if both are present and not done already + var offset = output.indexOf('OFFSET'); + var limit = output.indexOf('FETCH NEXT'); + if (offset != -1 && limit != -1 && offset > limit){ + var temp = [output[offset], output[offset+1], output[offset+2]]; + output[offset] = output[limit]; + output[offset+1] = output[limit+1]; + output[offset+2] = output[limit+2]; + output[limit] = temp[0]; + output[limit+1] = temp[1]; + output[limit+2] = temp[2]; + } + + return this.output; +}; + +Oracle.prototype.visitColumn = function(columnNode) { + var self=this; + var table; + var inSelectClause; + + function _arrayAgg(){ + throw new Error("Oracle does not support array_agg."); + } + + function _countStar(){ + // Implement our own since count(table.*) is invalid in Oracle + var result='COUNT(*)'; + if(inSelectClause && columnNode.alias) { + result += self._aliasText + self.quote(columnNode.alias); + } + return result; + } + + table = columnNode.table; + inSelectClause = !this._selectOrDeleteEndIndex; + if (isCountStarExpression(columnNode)) return _countStar(); + if (inSelectClause && table && !table.alias && columnNode.asArray) return _arrayAgg(); + return Oracle.super_.prototype.visitColumn.call(this, columnNode); +}; + + +Oracle.prototype.visitReturning = function() { + // TODO: need to add some code to the INSERT clause to support this since its the equivalent of the OUTPUT clause + // in MS SQL which appears before the values, not at the end of the statement. + throw new Error('Returning clause is not yet supported for Oracle.'); +}; + + +Oracle.prototype._getParameterValue = function(value) { + if (Buffer.isBuffer(value)) { + value = "utl_raw.cast_to_varchar2(hextoraw('" + value.toString('hex') + "'))"; + } else { + value = Oracle.super_.prototype._getParameterValue.call(this, value); + //value = Postgres.prototype._getParameterValue.call(this, value); + } + return value; +}; + + +Oracle.prototype.visitIndexes = function(node) { + + var tableName = this._queryNode.table.getName(); + var schemaName = this._queryNode.table.getSchema(); + + var indexes = "SELECT * FROM USER_INDEXES WHERE TABLE_NAME = '" + tableName + "'"; + + if (schemaName) { + indexes += " AND TABLE_OWNER = '" + schemaName + "'"; + } + + return indexes; +}; + + +Oracle.prototype.visitDropIndex = function(node) { + var result = [ 'DROP INDEX' ]; + var schemaName = node.table.getSchema(); + if (schemaName) { + result.push(this.quote(schemaName) + "."); + } + + result.push(this.quote(node.options.indexName)); + + return result; +}; + +// Using same CASE implementation as MSSQL +Oracle.prototype.visitCase = function(caseExp) { + + return Mssql.prototype.visitCase.call(this, caseExp); +}; + +// Using same JOIN implementation as MSSQL +Oracle.prototype.visitJoin = function(joinExp) { + return Mssql.prototype.visitJoin.call(this, joinExp); +}; + +Oracle.prototype.visitOnConflict = function(onConflict) { + throw new Error('Oracle does not allow onConflict clause.'); +}; + +function isCreateIfNotExists(create){ + if (create.nodes.length===0) return false; + if (create.nodes[0].type!='IF NOT EXISTS') return false; + return true; +} + +function isCreateTemporary(create){ + return create.options.isTemporary; +} + +function isDropIfExists(drop){ + if (drop.nodes.length===0) return false; + if (drop.nodes[0].type!='IF EXISTS') return false; + return true; +} + +// SQL Server does not support array expressions except in the IN clause. +function isRightSideArray(binary){ + return Array.isArray(binary.right); +} + +function isCountStarExpression(columnNode){ + if (!columnNode.aggregator) return false; + if (columnNode.aggregator.toLowerCase()!='count') return false; + if (!columnNode.star) return false; + return true; +} + +function isAlterAddColumn(alter){ + if (alter.nodes.length===0) return false; + if (alter.nodes[0].type!='ADD COLUMN') return false; + return true; +} + +function isAlterDropColumn(alter){ + if (alter.nodes.length===0) return false; + if (alter.nodes[0].type!='DROP COLUMN') return false; + return true; +} + +module.exports = Oracle; diff --git a/lib/dialect/postgres.js b/lib/dialect/postgres.js index 5319f8d7..9b81af25 100644 --- a/lib/dialect/postgres.js +++ b/lib/dialect/postgres.js @@ -6,9 +6,10 @@ var From = require('../node/from'); var Select = require('../node/select'); var Table = require('../table'); -var Postgres = function() { +var Postgres = function(config) { this.output = []; this.params = []; + this.config = config || {}; }; Postgres.prototype._myClass = Postgres; @@ -25,7 +26,7 @@ Postgres.prototype._getParameterText = function(index, value) { } }; -Postgres.prototype._getParameterValue = function(value) { +Postgres.prototype._getParameterValue = function(value, quoteChar) { // handle primitives if (null === value) { value = 'NULL'; @@ -35,20 +36,39 @@ Postgres.prototype._getParameterValue = function(value) { // number is just number value = value; } else if ('string' === typeof value) { - // string uses single quote - value = this.quote(value, "'"); + // string uses single quote by default + value = this.quote(value, quoteChar || "'"); } else if ('object' === typeof value) { - if (_.isArray(value)) { - // convert each element of the array - value = _.map(value, this._getParameterValue, this); - value = '(' + value.join(', ') + ')'; - } else if (_.isFunction(value.toISOString)) { + if (Array.isArray(value)) { + if (this._myClass === Postgres) { + // naive check to see if this is an array of objects, which + // is handled differently than an array of primitives + if (value.length && 'object' === typeof value[0] && + !_.isFunction(value[0].toISOString) && + !Array.isArray(value[0])) { + value = "'" + JSON.stringify(value) + "'"; + } else { + var self = this; + value = value.map(function (item) { + // In a Postgres array, strings must be double-quoted + return self._getParameterValue(item, '"'); + }); + value = '\'{' + value.join(',') + '}\''; + } + } else { + value = _.map(value, this._getParameterValue.bind(this)); + value = '(' + value.join(', ') + ')'; + } + } else if (value instanceof Date) { // Date object's default toString format does not get parsed well - // Handle date like objects using toISOString + // Handle dates using toISOString value = this._getParameterValue(value.toISOString()); + } else if (Buffer.isBuffer(value)) { + value = this._getParameterValue('\\x' + value.toString('hex')); } else { // rich object represent with string - value = this._getParameterValue(value.toString()); + var strValue = value.toString(); + value = strValue === '[object Object]' ? this._getParameterValue(JSON.stringify(value)) : this._getParameterValue(strValue); } } else { throw new Error('Unable to use ' + value + ' in query'); @@ -59,6 +79,7 @@ Postgres.prototype._getParameterValue = function(value) { }; Postgres.prototype._getParameterPlaceholder = function(index, value) { + /* jshint unused: false */ return '$' + index; }; @@ -69,6 +90,16 @@ Postgres.prototype.getQuery = function(queryNode) { } this.output = this.visit(queryNode); + //if is a create view, must replace paramaters with values + if (this.output.indexOf('CREATE VIEW') > -1) { + var previousFlagStatus = this._disableParameterPlaceholders; + this._disableParameterPlaceholders = true; + this.output = []; + this.output = this.visit(queryNode); + this.params = []; + this._disableParameterPlaceholders = previousFlagStatus; + } + // create the query object var query = { text: this.output.join(' '), values: this.params }; @@ -100,27 +131,41 @@ Postgres.prototype.visit = function(node) { case 'SUBQUERY' : return this.visitSubquery(node); case 'SELECT' : return this.visitSelect(node); case 'INSERT' : return this.visitInsert(node); + case 'REPLACE' : return this.visitReplace(node); case 'UPDATE' : return this.visitUpdate(node); - case 'DELETE' : return this.visitDelete(); + case 'DELETE' : return this.visitDelete(node); case 'CREATE' : return this.visitCreate(node); case 'DROP' : return this.visitDrop(node); + case 'TRUNCATE' : return this.visitTruncate(node); + case 'DISTINCT' : return this.visitDistinct(node); + case 'DISTINCT ON' : return this.visitDistinctOn(node); case 'ALIAS' : return this.visitAlias(node); case 'ALTER' : return this.visitAlter(node); + case 'CAST' : return this.visitCast(node); case 'FROM' : return this.visitFrom(node); case 'WHERE' : return this.visitWhere(node); case 'ORDER BY' : return this.visitOrderBy(node); - case 'ORDER BY COLUMN' : return this.visitOrderByColumn(node); + case 'ORDER BY VALUE' : return this.visitOrderByValue(node); case 'GROUP BY' : return this.visitGroupBy(node); case 'HAVING' : return this.visitHaving(node); case 'RETURNING' : return this.visitReturning(node); + case 'ONDUPLICATE' : return this.visitOnDuplicate(node); + case 'ONCONFLICT' : return this.visitOnConflict(node); + case 'FOR UPDATE' : return this.visitForUpdate(); + case 'FOR SHARE' : return this.visitForShare(); case 'TABLE' : return this.visitTable(node); case 'COLUMN' : return this.visitColumn(node); + case 'FOREIGN KEY' : return this.visitForeignKey(node); case 'JOIN' : return this.visitJoin(node); + case 'LITERAL' : return this.visitLiteral(node); case 'TEXT' : return node.text; case 'PARAMETER' : return this.visitParameter(node); case 'DEFAULT' : return this.visitDefault(node); case 'IF EXISTS' : return this.visitIfExists(); case 'IF NOT EXISTS' : return this.visitIfNotExists(); + case 'OR IGNORE' : return this.visitOrIgnore(); + case 'CASCADE' : return this.visitCascade(); + case 'RESTRICT' : return this.visitRestrict(); case 'RENAME' : return this.visitRename(node); case 'ADD COLUMN' : return this.visitAddColumn(node); case 'DROP COLUMN' : return this.visitDropColumn(node); @@ -129,11 +174,19 @@ Postgres.prototype.visit = function(node) { case 'CREATE INDEX' : return this.visitCreateIndex(node); case 'DROP INDEX' : return this.visitDropIndex(node); case 'FUNCTION CALL' : return this.visitFunctionCall(node); - - case 'POSTFIX UNARY' : return this.visitPostfixUnary(node); - case 'PREFIX UNARY' : return this.visitPrefixUnary(node); - case 'BINARY' : return this.visitBinary(node); - case 'TERNARY' : return this.visitTernary(node); + case 'ARRAY CALL' : return this.visitArrayCall(node); + case 'CREATE VIEW' : return this.visitCreateView(node); + case 'INTERVAL' : return this.visitInterval(node); + + case 'POSTFIX UNARY' : return this.visitPostfixUnary(node); + case 'PREFIX UNARY' : return this.visitPrefixUnary(node); + case 'BINARY' : return this.visitBinary(node); + case 'TERNARY' : return this.visitTernary(node); + case 'IN' : return this.visitIn(node); + case 'NOT IN' : return this.visitNotIn(node); + case 'CASE' : return this.visitCase(node); + case 'AT' : return this.visitAt(node); + case 'SLICE' : return this.visitSlice(node); case 'LIMIT' : case 'OFFSET': @@ -144,6 +197,8 @@ Postgres.prototype.visit = function(node) { }; Postgres.prototype._quoteCharacter = '"'; +Postgres.prototype._aliasText = ' AS '; + Postgres.prototype.quote = function(word, quoteCharacter) { var q; if (quoteCharacter) { @@ -152,13 +207,30 @@ Postgres.prototype.quote = function(word, quoteCharacter) { } else { q = this._quoteCharacter; } - - return q + word.replace(new RegExp(q,'g'),q+q) + q; + // handle square brackets specially + if (q=='['){ + return '['+word+']'; + } else { + return q + word.replace(new RegExp(q,'g'),q+q) + q; + } }; Postgres.prototype.visitSelect = function(select) { - var result = ['SELECT', select.nodes.map(this.visit.bind(this)).join(', ')]; + var result = ['SELECT']; + + if (select.isDistinct) result.push('DISTINCT'); + + var distinctOnNode = select.nodes.filter(function (node) {return node.type === 'DISTINCT ON';}).shift(); + var nonDistinctOnNodes = select.nodes.filter(function (node) {return node.type !== 'DISTINCT ON';}); + + if (distinctOnNode) { + result.push(this.visit(distinctOnNode)); + } + + result.push(nonDistinctOnNodes.map(this.visit.bind(this)).join(', ')); + this._selectOrDeleteEndIndex = this.output.length + result.length; + return result; }; @@ -167,28 +239,38 @@ Postgres.prototype.visitInsert = function(insert) { // don't use table.column for inserts this._visitedInsert = true; - var paramNodes = insert.getParameters() - .map(function (paramSet) { - return paramSet.map(function (param) { - return self.visit(param); + var result = ['INSERT']; + result = result.concat(insert.nodes.map(this.visit.bind(this))); + result.push('INTO ' + this.visit(this._queryNode.table.toNode())); + result.push('(' + insert.columns.map(this.visit.bind(this)).join(', ') + ')'); + + var paramNodes = insert.getParameters(); + + if (paramNodes.length > 0) { + var paramText = paramNodes.map(function (paramSet) { + return paramSet.map(function (param) { + return self.visit(param); + }).join(', '); + }).map(function (param) { + return '('+param+')'; }).join(', '); - }).map(function (param) { - return '('+param+')'; - }).join(', '); - var result = [ - 'INSERT INTO', - this.visit(this._queryNode.table.toNode()), - '(' + insert.columns.map(this.visit.bind(this)).join(', ') + ')', - 'VALUES', paramNodes - ]; + result.push('VALUES', paramText); - if (result.slice(2, 5).join(' ') === '() VALUES ()') { - result.splice(2, 3, 'DEFAULT VALUES'); + if (result.slice(2, 5).join(' ') === '() VALUES ()') { + result.splice(2, 3, 'DEFAULT VALUES'); + } } + + this._visitedInsert = false; + return result; }; +Postgres.prototype.visitReplace = function(replace) { + throw new Error('Postgres does not support REPLACE.'); +}; + Postgres.prototype.visitUpdate = function(update) { // don't auto-generate from clause var params = []; @@ -208,9 +290,13 @@ Postgres.prototype.visitUpdate = function(update) { return result; }; -Postgres.prototype.visitDelete = function() { - this._selectOrDeleteEndIndex = 1; - return ['DELETE']; +Postgres.prototype.visitDelete = function (del) { + var result = ['DELETE']; + if (del.nodes.length) { + result.push(del.nodes.map(this.visit.bind(this)).join(', ')); + } + this._selectOrDeleteEndIndex = result.length; + return result; }; Postgres.prototype.visitCreate = function(create) { @@ -218,11 +304,30 @@ Postgres.prototype.visitCreate = function(create) { // don't auto-generate from clause var table = this._queryNode.table; var col_nodes = table.columns.map(function(col) { return col.toNode(); }); + var foreign_key_nodes = table.foreignKeys; - var result = ['CREATE TABLE']; + var result = ['CREATE TABLE']; + if (create.options.isTemporary) result=['CREATE TEMPORARY TABLE']; result = result.concat(create.nodes.map(this.visit.bind(this))); result.push(this.visit(table.toNode())); - result.push('(' + col_nodes.map(this.visit.bind(this)).join(', ') + ')'); + var primary_col_nodes = col_nodes.filter(function(n) { + return n.primaryKey; + }); + this._visitCreateCompoundPrimaryKey = primary_col_nodes.length > 1; + var colspec = '(' + col_nodes.map(this.visit.bind(this)).join(', '); + if (this._visitCreateCompoundPrimaryKey) { + colspec += ', PRIMARY KEY ('; + colspec += primary_col_nodes.map(function(node) { + return this.quote(node.name); + }.bind(this)).join(', '); + colspec += ')'; + } + if(foreign_key_nodes.length > 0) { + colspec += ', ' + foreign_key_nodes.map(this.visit.bind(this)).join(', '); + } + colspec += ')'; + result.push(colspec); + this._visitCreateCompoundPrimaryKey = false; this._visitingCreate = false; return result; }; @@ -231,12 +336,26 @@ Postgres.prototype.visitDrop = function(drop) { // don't auto-generate from clause var result = ['DROP TABLE']; result = result.concat(drop.nodes.map(this.visit.bind(this))); - result.push(this.visit(this._queryNode.table.toNode())); return result; }; +Postgres.prototype.visitTruncate = function(truncate) { + var result = ['TRUNCATE TABLE']; + result = result.concat(truncate.nodes.map(this.visit.bind(this))); + return result; +}; + +Postgres.prototype.visitDistinct = function(truncate) { + // Nothing to do here since it's handled in the SELECT clause + return []; +}; + +Postgres.prototype.visitDistinctOn = function(distinctOn) { + return ['DISTINCT ON('+distinctOn.nodes.map(this.visit.bind(this)).join(', ')+')']; +}; + Postgres.prototype.visitAlias = function(alias) { - var result = [this.visit(alias.value) + ' AS ' + this.quote(alias.alias)]; + var result = [this.visit(alias.value) + this._aliasText + this.quote(alias.alias)]; return result; }; @@ -244,8 +363,6 @@ Postgres.prototype.visitAlter = function(alter) { this._visitingAlter = true; // don't auto-generate from clause var table = this._queryNode.table; - // TODO: col_nodes is unused? - var col_nodes = table.columns.map(function(col) { return col.toNode(); }); var result = [ 'ALTER TABLE', this.visit(table.toNode()), @@ -255,6 +372,13 @@ Postgres.prototype.visitAlter = function(alter) { return result; }; +Postgres.prototype.visitCast = function(cast) { + this._visitingCast = true; + var result = ['CAST(' + this.visit(cast.value) + ' AS ' + cast.dataType + ')']; + this._visitingCast = false; + return result; +}; + Postgres.prototype.visitFrom = function(from) { var result = []; if (from.skipFromStatement) { @@ -269,19 +393,24 @@ Postgres.prototype.visitFrom = function(from) { }; Postgres.prototype.visitWhere = function(where) { + this._visitingWhere = true; var result = ['WHERE', where.nodes.map(this.visit.bind(this)).join(', ')]; + this._visitingWhere = false; return result; }; Postgres.prototype.visitOrderBy = function(orderBy) { var result = ['ORDER BY', orderBy.nodes.map(this.visit.bind(this)).join(', ')]; + if (this._myClass === Postgres && this.config.nullOrder) { + result.push('NULLS ' + this.config.nullOrder.toUpperCase()); + } return result; }; -Postgres.prototype.visitOrderByColumn = function(column) { - var text = this.visit(column.column); - if (column.direction) { - text += ' ' + this.visit(column.direction); +Postgres.prototype.visitOrderByValue = function(orderByValue) { + var text = this.visit(orderByValue.value); + if (orderByValue.direction) { + text += ' ' + this.visit(orderByValue.direction); } return [text]; }; @@ -308,6 +437,10 @@ Postgres.prototype.visitPostfixUnary = function(unary) { Postgres.prototype.visitBinary = function(binary) { var self = this; + + binary.left.property = binary.left.name; + binary.right.property = binary.right.name; + var text = '(' + this.visit(binary.left) + ' ' + binary.operator + ' '; if (Array.isArray(binary.right)) { text += '(' + binary.right.map(function (node) { @@ -346,13 +479,143 @@ Postgres.prototype.visitTernary = function(ternary) { return [text]; }; +Postgres.prototype.visitIn = function(binary) { + var self = this; + var text = '('; + + if (Array.isArray(binary.right)) { + if (binary.right.length) { + var params = []; + var hasNull = false; + + binary.right.forEach(function(node) { + if (node.type === 'PARAMETER' && node._val === null) { + hasNull = true; + } else { + params.push(self.visit(node)); + } + }); + + if (params.length) { + text += this.visit(binary.left) + ' IN (' + params.join(', ') + ')'; + + if (hasNull) { + text += ' OR ' + this.visit(binary.left) + ' IS NULL'; + } + } else { // implicitely has null + text += this.visit(binary.left) + ' IS NULL'; + } + } else { + text += '1=0'; + } + } else { + text += this.visit(binary.left) + ' IN ' + this.visit(binary.right); + } + + text += ')'; + return [text]; +}; + +Postgres.prototype.visitNotIn = function(binary) { + var self = this; + var text = '('; + + if (Array.isArray(binary.right)) { + if (binary.right.length) { + var params = []; + var hasNull = false; + + binary.right.forEach(function(node) { + if (node.type === 'PARAMETER' && node._val === null) { + hasNull = true; + } else { + params.push(self.visit(node)); + } + }); + + if (params.length && hasNull) { + text += 'NOT ('; + text += this.visit(binary.left) + ' IN (' + params.join(', ') + ')'; + text += ' OR ' + this.visit(binary.left) + ' IS NULL'; + text += ')'; + } else if (params.length) { + text += this.visit(binary.left) + ' NOT IN (' + params.join(', ') + ')'; + } else { // implicitely has null + text += this.visit(binary.left) + ' IS NOT NULL'; + } + } else { + text += '1=1'; + } + } else { + text += this.visit(binary.left) + ' NOT IN ' + this.visit(binary.right); + } + + text += ')'; + return [text]; +}; + +Postgres.prototype.visitCase = function(caseExp) { + assert(caseExp.whenList.length == caseExp.thenList.length); + + var self = this; + var text = '(CASE'; + + this.visitingCase = true; + + for (var i = 0; i < caseExp.whenList.length; i++) { + var whenExp = ' WHEN ' + this.visit(caseExp.whenList[i]); + var thenExp = ' THEN ' + this.visit(caseExp.thenList[i]); + text += whenExp + thenExp; + } + + if (null !== caseExp.else && undefined !== caseExp.else) { + text += ' ELSE ' + this.visit(caseExp.else); + } + + this.visitingCase = false; + + text += ' END)'; + return [text]; +}; + +Postgres.prototype.visitAt = function(at) { + var text = '(' + this.visit(at.value) + ')[' + this.visit(at.index) + ']'; + return [text]; +}; + +Postgres.prototype.visitSlice = function(slice) { + var text = '(' + this.visit(slice.value) + ')'; + text += '[' + this.visit(slice.start) + ':' + this.visit(slice.end) + ']'; + return [text]; +}; + +Postgres.prototype.visitContains = function(contains) { + var text = this.visit(contains.value); + text += ' @> ' + this.visit(contains.set); + return [text]; +}; + +Postgres.prototype.visitContainedBy = function(containedBy) { + var text = this.visit(containedBy.value); + text += ' <@ ' + this.visit(containedBy.set); + return [text]; +}; + +Postgres.prototype.visitOverlap = function(overlap) { + var text = this.visit(overlap.value); + text += ' && ' + this.visit(overlap.set); + return [text]; +}; + Postgres.prototype.visitQuery = function(queryNode) { + if (this._queryNode) return this.visitSubquery(queryNode,dontParenthesizeSubQuery(this._queryNode)); this._queryNode = queryNode; // need to sort the top level query nodes on visitation priority // so select/insert/update/delete comes before from comes before where - var sortedNodes = []; var missingFrom = true; var hasFrom = false; + var createView; + var isSelect = false; var actions = []; var targets = []; var filters = []; @@ -360,14 +623,17 @@ Postgres.prototype.visitQuery = function(queryNode) { var node = queryNode.nodes[i]; switch(node.type) { case "SELECT": + isSelect = true; // jshint ignore:line case "DELETE": actions.push(node); break; case "INDEXES": case "INSERT": + case "REPLACE": case "UPDATE": case "CREATE": case "DROP": + case "TRUNCATE": case "ALTER": actions.push(node); missingFrom = false; @@ -378,6 +644,9 @@ Postgres.prototype.visitQuery = function(queryNode) { missingFrom = false; targets.push(node); break; + case "CREATE VIEW": + createView = node; + break; default: filters.push(node); break; @@ -386,13 +655,35 @@ Postgres.prototype.visitQuery = function(queryNode) { if(!actions.length) { // if no actions are given, guess it's a select actions.push(new Select().add('*')); + isSelect = true; } - if(missingFrom) { + if(missingFrom && queryNode.table instanceof Table) { + // the instanceof handles the situation where a sql.select(some expression) is used and there should be no FROM clause targets.push(new From().add(queryNode.table)); } + if (createView) { + if (isSelect) { + actions.unshift(createView); + } else { + throw new Error('Create View requires a Select.'); + } + } + return this.visitQueryHelper(actions,targets,filters); +}; + +/** + * We separate out this part of query building so it can be overridden by other implementations. + * + * @param {Node[]} actions + * @param {Node[]} targets + * @param {Node[]} filters + * @returns {String[]} + */ +Postgres.prototype.visitQueryHelper=function(actions,targets,filters){ + this.handleDistinct(actions, filters); // lazy-man sorting - sortedNodes = actions.concat(targets).concat(filters); - for(i = 0; i < sortedNodes.length; i++) { + var sortedNodes = actions.concat(targets).concat(filters); + for(var i = 0; i < sortedNodes.length; i++) { var res = this.visit(sortedNodes[i]); this.output = this.output.concat(res); } @@ -400,9 +691,9 @@ Postgres.prototype.visitQuery = function(queryNode) { return this.output; }; -Postgres.prototype.visitSubquery = function(queryNode) { +Postgres.prototype.visitSubquery = function(queryNode,dontParenthesize) { // create another query builder of the current class to build the subquery - var subQuery = new this._myClass(); + var subQuery = new this._myClass(this.config); // let the subquery modify this instance's params array subQuery.params = this.params; @@ -418,7 +709,10 @@ Postgres.prototype.visitSubquery = function(queryNode) { } var alias = queryNode.alias; - return ['(' + subQuery.output.join(' ') + ')' + (alias ? ' ' + alias : '')]; + if (dontParenthesize) { + return [subQuery.output.join(' ') + (alias ? ' ' + this.quote(alias) : '')]; + } + return ['(' + subQuery.output.join(' ') + ')' + (alias ? ' ' + this.quote(alias) : '')]; }; Postgres.prototype.visitTable = function(tableNode) { @@ -429,68 +723,242 @@ Postgres.prototype.visitTable = function(tableNode) { txt += '.'; } txt += this.quote(table.getName()); - if(table.alias) { - txt += ' AS ' + this.quote(table.alias); + if(typeof table.alias === 'string') { + txt += this._aliasText + this.quote(table.alias); } return [txt]; }; Postgres.prototype.visitColumn = function(columnNode) { var table = columnNode.table; - var inSelectClause = !this._selectOrDeleteEndIndex; - var txt = ""; + var inInsertUpdateClause = this._visitedInsert || this._visitedReplace || this._visitingUpdateTargetColumn; + var inDdlClause = this._visitingAddColumn || this._visitingAlter || this._visitingCreate; + var inSelectClause = + this.visitingReturning || + (!this._selectOrDeleteEndIndex + && !this._visitingWhere // jshint ignore:line + && !inInsertUpdateClause // jshint ignore:line + && !inDdlClause // jshint ignore:line + && !this.visitingCase // jshint ignore:line + && !this._visitingJoin // jshint ignore:line + ); + var inFunctionCall = this._visitingFunctionCall; + var inCast = this._visitingCast; + var txt = []; var closeParen = 0; - if(inSelectClause && !table.alias) { + if(inSelectClause && (table && !table.alias || !!columnNode.alias)) { if (columnNode.asArray) { closeParen++; - txt += this._arrayAggFunctionName+'('; + txt.push(this._arrayAggFunctionName+'('); } if (!!columnNode.aggregator) { closeParen++; - txt += columnNode.aggregator + '('; + txt.push(columnNode.aggregator + '('); } if (columnNode.distinct === true) { closeParen++; - txt += 'DISTINCT('; + txt.push('DISTINCT('); } } - if(!this._visitedInsert && !this._visitingUpdateTargetColumn && !this._visitingCreate && !this._visitingAlter) { - if(table.alias) { - txt = this.quote(table.alias); - } else { - if(table.getSchema()) { - txt = this.quote(table.getSchema()); - txt += '.'; + if(!inInsertUpdateClause && !this.visitingReturning && !this._visitingCreate && !this._visitingAlter && !columnNode.subfieldContainer) { + if (table) { + if (typeof table.alias === 'string') { + txt.push(this.quote(table.alias)); + } else { + if (table.getSchema()) { + txt.push(this.quote(table.getSchema())); + txt.push('.'); + } + txt.push(this.quote(table.getName())); } - txt += this.quote(table.getName()); + txt.push('.'); } - txt += '.'; } if (columnNode.star) { - txt += '*'; - } else { - txt += this.quote(columnNode.name); + var allCols = []; + var hasAliases = false; + if(columnNode.aggregator !== 'COUNT') { + var tableName = txt.join(''); + for (var i = 0; i < table.columns.length; ++i) { + var col = table.columns[i]; + var aliased = col.name !== (col.alias || col.property); + hasAliases = hasAliases || aliased; + allCols.push(tableName + this.quote(col.name) + (aliased ? this._aliasText + this.quote(col.alias || col.property) : '')); + } + } + if(hasAliases) { + txt = [allCols.join(', ')]; + } + else { + txt.push('*'); + } + } + else if (columnNode.isConstant) { + // this injects directly into SELECT statement rather than creating a parameter + // txt.push(this._getParameterValue(columnNode.literalValue)) + // currently thinking it is better to generate a parameter + var value = columnNode.constantValue; + this.params.push(value); + txt.push(this._getParameterText(this.params.length, value)); + } + else { + if (columnNode.subfieldContainer) { + txt.push('(' + this.visitColumn(columnNode.subfieldContainer) + ').'); + } + txt.push(this.quote(columnNode.name)); } if(closeParen) { - for(var i = 0; i < closeParen; i++) { - txt += ')'; + for(var j = 0; j < closeParen; j++) { + txt.push(')'); } } - if(inSelectClause && columnNode.alias) { - txt += ' AS ' + this.quote(columnNode.alias); + if(inSelectClause && !inFunctionCall && !inCast && (columnNode.alias || columnNode.property !== columnNode.name)) { + txt.push(this._aliasText + this.quote(columnNode.alias || columnNode.property)); } if(this._visitingCreate || this._visitingAddColumn) { assert(columnNode.dataType, 'dataType missing for column ' + columnNode.name + ' (CREATE TABLE and ADD COLUMN statements require a dataType)'); - txt += ' ' + columnNode.dataType; + txt.push(' ' + columnNode.dataType); + + if (this._visitingCreate) { + if (columnNode.primaryKey && !this._visitCreateCompoundPrimaryKey) { + // creating a column as a primary key + txt.push(' PRIMARY KEY'); + } else if (columnNode.notNull) { + txt.push(' NOT NULL'); + } + if (!columnNode.primaryKey && columnNode.unique) { + txt.push(' UNIQUE'); + } + if (columnNode.defaultValue !== undefined) { + txt.push(' DEFAULT ' + this._getParameterValue(columnNode.defaultValue)); + } + } + + if (!!columnNode.references) { + assert.equal(typeof (columnNode.references), 'object', + 'references is not a object for column ' + columnNode.name + + ' (REFERENCES statements within CREATE TABLE and ADD COLUMN statements' + + ' require refrences to be expressed as an object)'); + + //Empty refrence objects are ok + if (Object.keys(columnNode.references).length > 0){ + assert(columnNode.references.table, 'reference.table missing for column ' + + columnNode.name + + ' (REFERENCES statements within CREATE TABLE and ADD COLUMN statements' + + ' require a table and column)'); + assert(columnNode.references.column, 'reference.column missing for column ' + + columnNode.name + + ' (REFERENCES statements within CREATE TABLE and ADD COLUMN statements' + + ' require a table and column)'); + txt.push(' REFERENCES '); + if(columnNode.references.schema) { + txt.push(this.quote(columnNode.references.schema) + '.'); + } + txt.push(this.quote(columnNode.references.table) + '(' + + this.quote(columnNode.references.column) + ')'); + + var onDelete = columnNode.references.onDelete; + if (onDelete) onDelete = onDelete.toUpperCase(); + if (onDelete === 'CASCADE' || onDelete === 'RESTRICT' || onDelete === 'SET NULL' || onDelete === 'SET DEFAULT' || onDelete === 'NO ACTION') { + txt.push(' ON DELETE ' + onDelete); + } + var onUpdate = columnNode.references.onUpdate; + if (onUpdate) onUpdate = onUpdate.toUpperCase(); + if (onUpdate === 'CASCADE' || onUpdate === 'RESTRICT' || onUpdate === 'SET NULL' || onUpdate === 'SET DEFAULT' || onUpdate === 'NO ACTION') { + txt.push(' ON UPDATE ' + onUpdate); + } + var constraint = columnNode.references.constraint; + if (constraint) { + constraint = ' ' + constraint.toUpperCase(); + txt.push(constraint); + } + } + } } - return [txt]; + return [txt.join('')]; +}; + +Postgres.prototype.visitForeignKey = function(foreignKeyNode) +{ + var txt = []; + if(this._visitingCreate) { + assert(foreignKeyNode.table, 'Foreign table missing for table reference'); + assert(foreignKeyNode.columns, 'Columns missing for table reference'); + if(foreignKeyNode.refColumns !== undefined) { + assert.equal(foreignKeyNode.columns.length, foreignKeyNode.refColumns.length, 'Number of local columns and foreign columns differ in table reference'); + } + if(foreignKeyNode.name !== undefined) { + txt.push('CONSTRAINT ' + this.quote(foreignKeyNode.name) + ' '); + } + txt.push('FOREIGN KEY ( '); + for(var i = 0; i < foreignKeyNode.columns.length; i++) { + if(i>0) { + txt.push(', '); + } + txt.push(this.quote(foreignKeyNode.columns[i])); + } + txt.push(' ) REFERENCES '); + if(foreignKeyNode.schema !== undefined) { + txt.push(this.quote(foreignKeyNode.schema) + '.'); + } + txt.push(this.quote(foreignKeyNode.table)); + if(foreignKeyNode.refColumns !== undefined) { + txt.push(' ( '); + for(i = 0; i < foreignKeyNode.refColumns.length; i++) { + if(i>0) { + txt.push(', '); + } + txt.push(this.quote(foreignKeyNode.refColumns[i])); + } + txt.push(' )'); + } + var onDelete = foreignKeyNode.onDelete; + if(onDelete) { + onDelete = onDelete.toUpperCase(); + if(onDelete === 'CASCADE' || onDelete === 'RESTRICT' || onDelete === 'SET NULL' || onDelete === 'SET DEFAULT' || onDelete === 'NO ACTION') { + txt.push(' ON DELETE ' + onDelete); + } + } + var onUpdate = foreignKeyNode.onUpdate; + if(onUpdate) { + onUpdate = onUpdate.toUpperCase(); + if(onUpdate === 'CASCADE' || onUpdate === 'RESTRICT' || onUpdate === 'SET NULL' || onUpdate === 'SET DEFAULT' || onUpdate === 'NO ACTION') { + txt.push(' ON UPDATE ' + onUpdate); + } + } + if(foreignKeyNode.constraint) { + txt.push(' ' + foreignKeyNode.constraint.toUpperCase()); + } + } + return [txt.join('')]; }; Postgres.prototype.visitFunctionCall = function(functionCall) { - var txt = functionCall.name + '(' + functionCall.nodes.map(this.visit.bind(this)).join(', ') + ')'; + this._visitingFunctionCall = true; + var _this = this; + + function _extract() { + var nodes = functionCall.nodes.map(_this.visit.bind(_this)); + if (nodes.length != 1) throw new Error('Not enough parameters passed to ' + functionCall.name + ' function'); + var txt = 'EXTRACT(' + functionCall.name + ' FROM ' + (nodes[0]+'') + ')'; + return txt; + } + + var txt = ""; + // Override date functions since postgres (and others) uses extract + if (['YEAR', 'MONTH', 'DAY', 'HOUR'].indexOf(functionCall.name) >= 0) txt = _extract(); + // Override CURRENT_TIMESTAMP function to remove parens + else if ('CURRENT_TIMESTAMP' == functionCall.name) txt = functionCall.name; + else txt = functionCall.name + '(' + functionCall.nodes.map(this.visit.bind(this)).join(', ') + ')'; + this._visitingFunctionCall = false; + return [txt]; +}; + +Postgres.prototype.visitArrayCall = function(arrayCall) { + var txt = 'ARRAY[' + arrayCall.nodes.map(this.visit.bind(this)).join(', ') + ']'; return [txt]; }; @@ -498,10 +966,11 @@ Postgres.prototype.visitParameter = function(parameter) { // save the value into the parameters array var value = parameter.value(); this.params.push(value); - return [this._getParameterText(this.params.length, value)]; + return parameter.isExplicit ? [] : [this._getParameterText(this.params.length, value)]; }; Postgres.prototype.visitDefault = function(parameter) { + /* jshint unused: false */ return ['DEFAULT']; }; @@ -532,8 +1001,30 @@ Postgres.prototype.visitIfNotExists = function() { return ['IF NOT EXISTS']; }; +Postgres.prototype.visitOrIgnore = function() { + throw new Error('PostgreSQL does not allow orIgnore clause.'); +}; + +Postgres.prototype.visitCascade = function() { + return ['CASCADE']; +}; + +Postgres.prototype.visitRestrict = function() { + return ['RESTRICT']; +}; + +Postgres.prototype.visitForUpdate = function() { + return ['FOR UPDATE']; +}; + +Postgres.prototype.visitForShare = function() { + return ['FOR SHARE']; +}; + Postgres.prototype.visitJoin = function(join) { + if (join.subType === 'LEFT LATERAL') return this.visitLeftJoinLateral(join); var result = []; + this._visitingJoin = true; result = result.concat(this.visit(join.from)); result = result.concat(join.subType + ' JOIN'); result = result.concat(this.visit(join.to)); @@ -542,8 +1033,65 @@ Postgres.prototype.visitJoin = function(join) { return result; }; +Postgres.prototype.visitLeftJoinLateral = function(join) { + var result = []; + this._visitingJoin = true; + result = result.concat(this.visit(join.from)); + result = result.concat('LEFT JOIN LATERAL'); + result = result.concat(this.visit(join.to)); + result = result.concat('ON true'); + return result; +}; + +Postgres.prototype.visitLiteral = function(node) { + var txt = [node.literal]; + if(node.alias) { + txt.push(this._aliasText + this.quote(node.alias)); + } + return [txt.join('')]; +}; + Postgres.prototype.visitReturning = function(returning) { - return ['RETURNING', returning.nodes.map(this.visit.bind(this)).join(', ')]; + this.visitingReturning = true; + var r = ['RETURNING', returning.nodes.map(this.visit.bind(this)).join(', ')]; + this.visitingReturning = false; + + return r; +}; + +Postgres.prototype.visitOnDuplicate = function(onDuplicate) { + throw new Error('PostgreSQL does not allow onDuplicate clause.'); +}; + +Postgres.prototype.visitOnConflict = function(onConflict) { + var result = ['ON CONFLICT']; + var columns = []; + var updateClause = [], i, col; + var table = this._queryNode.table; + if(onConflict.constraint) + result.push(['ON CONSTRAINT', this.quote(onConflict.constraint)].join(' ')); + else if(onConflict.columns) { + for(i=0; i < onConflict.columns.length; i++) { + columns.push(this.quote(table.getColumn(onConflict.columns[i]).name)); + } + result.push( '(' + columns.join(', ') + ')' ); + } + + if(onConflict.update){ + updateClause.push("DO UPDATE SET"); + var update = onConflict.update; + var setClause = []; + for(i=0; i 0) { + var paramText = paramNodes.map(function (paramSet) { + return paramSet.map(function (param) { + return self.visit(param); + }).join(', '); + }).map(function (param) { + return '('+param+')'; + }).join(', '); + + result.push('VALUES', paramText); + + if (result.slice(2, 5).join(' ') === '() VALUES ()') { + result.splice(2, 3, 'DEFAULT VALUES'); + } + } + + this._visitedReplace = false; + + return result; +}; + +Sqlite.prototype._getParameterValue = function(value) { + if (Buffer.isBuffer(value)) { + value = 'x' + this._getParameterValue(value.toString('hex')); + } else if (value instanceof Date && this.config.dateTimeMillis) { + value = value.getTime(); + } else if('boolean' === typeof value) { + value = value ? 1 : 0; + } else if(_.isArray(value)) { + value = Postgres.prototype._getParameterValue.call(this, JSON.stringify(value)); + } else { + value = Postgres.prototype._getParameterValue.call(this, value); + } + return value; +}; + +Sqlite.prototype.visitDefault = function() { throw new Error('SQLite requires that all rows of a multi-row insert are for the same columns.'); }; -/*jshint unused: false */ -Sqlite.prototype.visitDropColumn = function(dropColumn) { +Sqlite.prototype.visitDropColumn = function() { throw new Error('SQLite does not allow dropping columns.'); }; -/*jshint unused: false */ -Sqlite.prototype.visitRenameColumn = function(renameColumn) { +Sqlite.prototype.visitFunctionCall = function (functionCall) { + var _this = this; + + this._visitingFunctionCall = true; + + function _left() { + // convert LEFT(column,4) to SUBSTR(column,1,4) + var nodes = functionCall.nodes.map(_this.visit.bind(_this)); + if (nodes.length != 2) throw new Error('Not enough parameters passed to LEFT function.'); + var txt = "SUBSTR(" + (nodes[0] + '') + ', 1, ' + (nodes[1] + '') + ')'; + return txt; + } + + function _right() { + // convert RIGHT(column,4) to SUBSTR(column,-4) + var nodes = functionCall.nodes.map(_this.visit.bind(_this)); + if (nodes.length != 2) throw new Error('Not enough parameters passed to RIGHT function.'); + var txt = "SUBSTR(" + (nodes[0] + '') + ', -' + (nodes[1] + '') + ')'; + return txt; + } + + function _extract() { + var nodes = functionCall.nodes.map(_this.visit.bind(_this)); + if (nodes.length != 1) throw new Error('Not enough parameters passed to ' + functionCall.name + ' function'); + var format; + switch (functionCall.name) { + case 'YEAR': + format = "'%Y'"; + break; + case 'MONTH': + format = "'%m'"; + break; + case 'DAY': + format = "'%d'"; + break; + case 'HOUR': + format = "'%H'"; + break; + } + var col = (nodes[0] + ''); + if (_this.config.dateTimeMillis) { + // Convert to a datetime before running the strftime function + // Sqlite unix epoch is in seconds, but javascript is milliseconds. + col = 'datetime(' + col + '/1000, "unixepoch")'; + } + var txt = 'strftime(' + format + ', ' + col + ')'; + return txt; + } + + var txt = ""; + var name = functionCall.name; + // Override LEFT and RIGHT and convert to SUBSTR + if (name == "LEFT") txt = _left(); + else if (name == "RIGHT") txt = _right(); + // Override date functions since sqlite uses strftime + else if (['YEAR', 'MONTH', 'DAY', 'HOUR'].indexOf(functionCall.name) >= 0) txt = _extract(); + else if ('CURRENT_TIMESTAMP' == functionCall.name) txt = functionCall.name; + else txt = name + '(' + functionCall.nodes.map(this.visit.bind(this)).join(', ') + ')'; + + this._visitingFunctionCall = false; + return [txt]; +}; + +Sqlite.prototype.visitTruncate = function(truncate) { + var result = ['DELETE FROM']; + result = result.concat(truncate.nodes.map(this.visit.bind(this))); + return result; +}; + +Sqlite.prototype.visitRenameColumn = function() { throw new Error('SQLite does not allow renaming columns.'); }; +Sqlite.prototype.visitOnDuplicate = function() { + throw new Error('SQLite does not allow onDuplicate clause.'); +}; + +Sqlite.prototype.visitOnConflict = function(onConflict) { + throw new Error('Sqlite does not allow onConflict clause.'); +}; + +Sqlite.prototype.visitReturning = function() { + throw new Error('SQLite does not allow returning clause.'); +}; + +Sqlite.prototype.visitForUpdate = function() { + throw new Error('SQLite does not allow FOR UPDATE clause.'); +}; + +Sqlite.prototype.visitForShare = function() { + throw new Error('SQLite does not allow FOR SHARE clause.'); +}; + Sqlite.prototype.visitAddColumn = function(addColumn) { assert(!this._hasAddedAColumn, 'SQLite can not add more that one column at a time'); var result = Postgres.prototype.visitAddColumn.call(this, addColumn); @@ -40,8 +176,30 @@ Sqlite.prototype.visitAddColumn = function(addColumn) { }; Sqlite.prototype.visitIndexes = function(node) { - var tableName = this.visit(this._queryNode.table.toNode()); + var tableName = this.visit(this._queryNode.table.toNode())[0]; return "PRAGMA INDEX_LIST(" + tableName + ")"; }; +Sqlite.prototype.visitCascade = function() { + throw new Error('Sqlite do not support CASCADE in DROP TABLE'); +}; + +Sqlite.prototype.visitRestrict = function() { + throw new Error('Sqlite do not support RESTRICT in DROP TABLE'); +}; + +Sqlite.prototype.visitBinary = function(binary) { + if(binary.operator === '@@'){ + binary.operator = 'MATCH'; + var ret = Sqlite.super_.prototype.visitBinary.call(this, binary); + binary.operator = '@@'; + return ret; + } + return Sqlite.super_.prototype.visitBinary.call(this, binary); +}; + +Sqlite.prototype.visitOrIgnore = function() { + return ['OR IGNORE']; +}; + module.exports = Sqlite; diff --git a/lib/functions.js b/lib/functions.js index 503980df..fe9cdaaf 100644 --- a/lib/functions.js +++ b/lib/functions.js @@ -1,22 +1,23 @@ 'use strict'; var _ = require('lodash'); var sliced = require('sliced'); -var FunctionCall = require(__dirname + '/node/functionCall'); +var FunctionCall = require('./node/functionCall'); // create a function that creates a function call of the specific name, using the specified sql instance -var getFunctionCallCreator = function(name, sql) { +var getFunctionCallCreator = function(name) { return function() { // turn array-like arguments object into a true array - var functionCall = new FunctionCall(name, sliced(arguments)); - functionCall.sql = sql; - return functionCall; + return new FunctionCall(name, sliced(arguments)); }; }; // creates a hash of functions for a sql instance -var getFunctions = function(functionNames, sql) { +var getFunctions = function(functionNames) { + if (typeof functionNames === 'string') + return getFunctionCallCreator(functionNames); + var functions = _.reduce(functionNames, function(reducer, name) { - reducer[name] = getFunctionCallCreator(name, sql); + reducer[name] = getFunctionCallCreator(name); return reducer; }, {}); return functions; @@ -35,11 +36,13 @@ var aggregateFunctions = [ // common scalar functions available to most databases var scalarFunctions = [ 'ABS', - 'COALESC', + 'COALESCE', + 'LEFT', 'LENGTH', 'LOWER', 'LTRIM', 'RANDOM', + 'RIGHT', 'ROUND', 'RTRIM', 'SUBSTR', @@ -47,11 +50,26 @@ var scalarFunctions = [ 'UPPER' ]; -var standardFunctionNames = aggregateFunctions.concat(scalarFunctions); +var dateFunctions = [ + 'YEAR', + 'MONTH', + 'DAY', + 'HOUR', + 'CURRENT_TIMESTAMP' +]; + +// hstore function available to Postgres +var hstoreFunction = 'HSTORE'; + +//text search functions available to Postgres +var textsearchFunctions = ['TS_RANK','TS_RANK_CD', 'PLAINTO_TSQUERY', 'TO_TSQUERY', 'TO_TSVECTOR', 'SETWEIGHT']; + +var standardFunctionNames = aggregateFunctions.concat(scalarFunctions).concat(hstoreFunction).concat(textsearchFunctions).concat(dateFunctions); // creates a hash of standard functions for a sql instance -var getStandardFunctions = function(sql) { - return getFunctions(standardFunctionNames, sql); +var getStandardFunctions = function() { + return getFunctions(standardFunctionNames); }; +module.exports.getFunctions = getFunctions; module.exports.getStandardFunctions = getStandardFunctions; diff --git a/lib/index.js b/lib/index.js index 4ab93b01..f26ad827 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,23 +1,26 @@ 'use strict'; var _ = require('lodash'); +var Column = require("./column"); var FunctionCall = require('./node/functionCall'); +var ArrayCall = require('./node/arrayCall'); var functions = require('./functions'); var getDialect = require('./dialect'); +var JoinNode = require('./node/join'); var Query = require('./node/query'); var sliced = require('sliced'); var Table = require('./table'); +var Interval = require('./node/interval'); // default dialect is postgres var DEFAULT_DIALECT = 'postgres'; -var Sql = function(dialect) { - dialect = dialect || DEFAULT_DIALECT; - - this.setDialect(dialect); +var Sql = function(dialect, config) { + this.setDialect(dialect || DEFAULT_DIALECT, config); // attach the standard SQL functions to this instance - this.functions = functions.getStandardFunctions(this); + this.functions = functions.getStandardFunctions(); + this.function = functions.getFunctions; }; // Define a table @@ -31,14 +34,18 @@ Sql.prototype.define = function(def) { // Returns a function call creator Sql.prototype.functionCallCreator = function(name) { - var sql = this; return function() { - var functionCall = new FunctionCall(name, sliced(arguments)); - functionCall.sql = sql; - return functionCall; + return new FunctionCall(name, sliced(arguments)); }; }; +// Returns a bracketed call creator literal +Sql.prototype.array = function() { + var arrayCall = new ArrayCall(sliced(arguments)); + arrayCall.sql = this; + return arrayCall; +}; + // Returns a select statement Sql.prototype.select = function() { var query = new Query({sql: this}); @@ -46,18 +53,52 @@ Sql.prototype.select = function() { return query; }; +// Returns a subQuery clause +Sql.prototype.subQuery = function(alias) { + // create the query and pass it off + var query = new Query(this); + query.type = 'SUBQUERY'; + query.alias = alias; + query.join = function(other) { + return new JoinNode('INNER', this.toNode(), other.toNode(), other); + }; + return query; +}; + +// Returns an interval clause +Sql.prototype.interval = function() { + var interval = new Interval(sliced(arguments)); + return interval; +}; + // Set the dialect -Sql.prototype.setDialect = function(dialect) { - this.dialect = getDialect(dialect); +Sql.prototype.setDialect = function(dialect, config) { + this.dialect = getDialect(dialect); + this.dialectName = dialect; + this.config = config; + return this; }; +// Create a constant Column (for use in SELECT) +Sql.prototype.constant = function(value) { + var config={ + name:"constant", + property:"constant", + isConstant:true, + constantValue:value, + }; + var cn = new Column(config); + return cn; +}; + + // back compat shim for the Sql class constructor -var create = function(dialect) { - return new Sql(dialect); +var create = function(dialect, config) { + return new Sql(dialect, {}); }; -module.exports = new Sql(DEFAULT_DIALECT); +module.exports = new Sql(DEFAULT_DIALECT, {}); module.exports.create = create; module.exports.Sql = Sql; module.exports.Table = Table; diff --git a/lib/joiner.js b/lib/joiner.js index 249eb1ea..5ce82cf9 100644 --- a/lib/joiner.js +++ b/lib/joiner.js @@ -1,7 +1,9 @@ +'use strict'; + var getPrimaryKeyColumn = function(table) { - for(var i = 0; i < table.columns.length; i++) { + for (var i = 0; i < table.columns.length; i++) { var col = table.columns[i]; - if(col.primaryKey) { + if (col.primaryKey) { return col; } } @@ -9,11 +11,11 @@ var getPrimaryKeyColumn = function(table) { var findReference = function(left, right) { // find reference - for(var i = 0; i < right.columns.length; i++) { + for (var i = 0; i < right.columns.length; i++) { var col = right.columns[i]; - if(col.references) { + if (col.references) { var leftName = left.getName(); - if(col.references == leftName || col.references.table == leftName) { + if (col.references === leftName || col.references.table === leftName) { var leftCol = left[col.references.column] || getPrimaryKeyColumn(left); return { left: leftCol, @@ -29,9 +31,8 @@ module.exports = { // requires one column to have { references: {table: 'foreignTableName', column: 'foreignColumnName'}} // or to have { references: 'foreignTableName'} -- in which case the foreign table's primary key is assumed leftJoin: function(left, right) { - var leftCol, rightCol; var ref = findReference(left, right); - if(!ref) { + if (!ref) { ref = findReference(right, left); } return left.join(right).on(ref.left.equals(ref.right)); diff --git a/lib/node/addColumn.js b/lib/node/addColumn.js index 273d8836..3d7d4066 100644 --- a/lib/node/addColumn.js +++ b/lib/node/addColumn.js @@ -1,6 +1,6 @@ 'use strict'; -var Node = require(__dirname); +var Node = require('./index'); module.exports = Node.define({ type: 'ADD COLUMN' diff --git a/lib/node/alias.js b/lib/node/alias.js index 78f01323..9f9855f5 100644 --- a/lib/node/alias.js +++ b/lib/node/alias.js @@ -1,7 +1,7 @@ 'use strict'; var _ = require('lodash'); -var Node = require(__dirname); +var Node = require('./index'); var AliasNode = Node.define({ type: 'ALIAS', diff --git a/lib/node/alter.js b/lib/node/alter.js index ce53623c..4aae4d83 100644 --- a/lib/node/alter.js +++ b/lib/node/alter.js @@ -1,6 +1,6 @@ 'use strict'; -var Node = require(__dirname); +var Node = require('./index'); module.exports = Node.define({ type: 'ALTER' diff --git a/lib/node/arrayCall.js b/lib/node/arrayCall.js new file mode 100644 index 00000000..7533190d --- /dev/null +++ b/lib/node/arrayCall.js @@ -0,0 +1,24 @@ +'use strict'; + +var _ = require('lodash'); +var Node = require('./index'); +var ParameterNode = require('./parameter'); +var valueExpressionMixin = require('./valueExpression'); + +var ArrayCallNode = Node.define({ + type: 'ARRAY CALL', + constructor: function(args) { + Node.call(this); + args = _.flatten(args); + this.addAll(args.map(ParameterNode.getNodeOrParameterNode)); + } +}); + +// mix in value expression +_.extend(ArrayCallNode.prototype, valueExpressionMixin()); + +// allow aliasing +var AliasNode = require('./alias'); +_.extend(ArrayCallNode.prototype, AliasNode.AliasMixin); + +module.exports = ArrayCallNode; diff --git a/lib/node/at.js b/lib/node/at.js new file mode 100644 index 00000000..5e8ca47b --- /dev/null +++ b/lib/node/at.js @@ -0,0 +1,27 @@ +'use strict'; + +var _ = require('lodash'); +var Node = require('./index'); +var valueExpressionMixin = require('./valueExpression'); + +var valueExpressionMixed = false; +var AtNode = Node.define({ + type: 'AT', + constructor: function(value, index) { + Node.call(this); + this.value = value; + this.index = index; + // Delay mixin to runtime, when all nodes have been defined, and + // mixin only once. ValueExpressionMixin has circular dependencies. + if (!valueExpressionMixed) { + valueExpressionMixed = true; + _.extend(AtNode.prototype, valueExpressionMixin()); + } + } +}); + +// allow aliasing +var AliasNode = require('./alias'); +_.extend(AtNode.prototype, AliasNode.AliasMixin); + +module.exports = AtNode; diff --git a/lib/node/binary.js b/lib/node/binary.js index 7b721650..da8eb8bd 100644 --- a/lib/node/binary.js +++ b/lib/node/binary.js @@ -1,8 +1,8 @@ 'use strict'; var _ = require('lodash'); -var Node = require(__dirname); -var valueExpressionMixin = require(__dirname + '/valueExpression'); +var Node = require('./index'); +var valueExpressionMixin = require('./valueExpression'); var valueExpressionMixed = false; var BinaryNode = Node.define(_.extend({ @@ -23,7 +23,7 @@ var BinaryNode = Node.define(_.extend({ })); // allow aliasing -var AliasNode = require(__dirname + '/alias'); +var AliasNode = require('./alias'); _.extend(BinaryNode.prototype, AliasNode.AliasMixin); module.exports = BinaryNode; diff --git a/lib/node/cascade.js b/lib/node/cascade.js new file mode 100644 index 00000000..4b6646fd --- /dev/null +++ b/lib/node/cascade.js @@ -0,0 +1,7 @@ +'use strict'; + +var Node = require('./index'); + +module.exports = Node.define({ + type: 'CASCADE' +}); diff --git a/lib/node/case.js b/lib/node/case.js new file mode 100644 index 00000000..ac3bf214 --- /dev/null +++ b/lib/node/case.js @@ -0,0 +1,29 @@ +'use strict'; + +var _ = require('lodash'); +var Node = require('./index'); +var valueExpressionMixin = require('./valueExpression'); + +var valueExpressionMixed = false; +var CaseNode = Node.define(_.extend({ + type: 'CASE', + constructor: function(config) { + Node.call(this); + this.whenList = config.whenList; + this.thenList = config.thenList; + this.else = config.else; + + // Delay mixin to runtime, when all nodes have been defined, and + // mixin only once. ValueExpressionMixin has circular dependencies. + if (!valueExpressionMixed) { + valueExpressionMixed = true; + _.extend(CaseNode.prototype, valueExpressionMixin()); + } + }, +})); + +// allow aliasing +var AliasNode = require('./alias'); +_.extend(CaseNode.prototype, AliasNode.AliasMixin); + +module.exports = CaseNode; diff --git a/lib/node/cast.js b/lib/node/cast.js new file mode 100644 index 00000000..be915b1e --- /dev/null +++ b/lib/node/cast.js @@ -0,0 +1,27 @@ +'use strict'; + +var _ = require('lodash'); +var Node = require('./index'); +var valueExpressionMixin = require('./valueExpression'); + +var valueExpressionMixed = false; +var CastNode = Node.define({ + type: 'CAST', + constructor: function(value, dataType) { + Node.call(this); + this.value = value; + this.dataType = dataType; + // Delay mixin to runtime, when all nodes have been defined, and + // mixin only once. ValueExpressionMixin has circular dependencies. + if (!valueExpressionMixed) { + valueExpressionMixed = true; + _.extend(CastNode.prototype, valueExpressionMixin()); + } + } +}); + +// allow aliasing +var AliasNode = require('./alias'); +_.extend(CastNode.prototype, AliasNode.AliasMixin); + +module.exports = CastNode; diff --git a/lib/node/column.js b/lib/node/column.js index 9216d1bc..6b145d74 100644 --- a/lib/node/column.js +++ b/lib/node/column.js @@ -1,20 +1,33 @@ 'use strict'; -var Node = require(__dirname); +var Node = require('./index'); module.exports = Node.define({ type: 'COLUMN', constructor: function(config) { Node.call(this); this.name = config.name; + this.property = config.property || config.name; this.alias = config.alias; this.star = config.star; + this.isConstant = config.isConstant; + this.constantValue = config.constantValue; this.asArray = config.asArray; this.aggregator = config.aggregator; this.table = config.table; this.value = config.getValue(); this.dataType = config.dataType; this.distinct = config.distinct; + this.primaryKey = config.primaryKey; + this.notNull = config.notNull; + this.defaultValue = config.defaultValue; + this.references = config.references; + // If subfieldContainer is present, this is a subfield and subfieldContainer + // is the parent Column + this.subfieldContainer = config.subfieldContainer; + this.subfields = config.subfields; + this.autoGenerated = !!config.autoGenerated; + this.unique = !!config.unique; }, as: function(alias) { this.alias = alias; diff --git a/lib/node/create.js b/lib/node/create.js index bf535f6a..67cd9b3d 100644 --- a/lib/node/create.js +++ b/lib/node/create.js @@ -1,7 +1,14 @@ 'use strict'; -var Node = require(__dirname); +var Node = require('./index'); module.exports = Node.define({ - type: 'CREATE' + type: 'CREATE', + + constructor: function(isTemporary) { + Node.call(this); + + this.options = { isTemporary: isTemporary}; + }, + }); diff --git a/lib/node/createIndex.js b/lib/node/createIndex.js index b4ca484d..8f8f0d04 100644 --- a/lib/node/createIndex.js +++ b/lib/node/createIndex.js @@ -1,6 +1,7 @@ 'use strict'; -var Node = require(__dirname); +var Node = require('./'); +var sliced = require('sliced'); module.exports = Node.define({ type: 'CREATE INDEX', @@ -8,11 +9,8 @@ module.exports = Node.define({ constructor: function(table, indexName) { Node.call(this); - this.table = table; - this.names = []; - this.columns = []; - this.valueSets = []; - this.options = { indexName: indexName, columns: [] }; + this.table = table; + this.options = { indexName: indexName, columns: [] }; }, unique: function() { @@ -36,7 +34,7 @@ module.exports = Node.define({ }, on: function() { - var args = Array.prototype.slice.call(arguments); + var args = sliced(arguments); this.options.columns = this.options.columns.concat(args); return this; }, @@ -51,7 +49,8 @@ module.exports = Node.define({ if (!result) { var columns = this.options.columns.map(function(col) { - return col.name; + var column = col.name ? col.name : col.value.name; + return column; }).sort(); result = [this.table._name]; diff --git a/lib/node/createView.js b/lib/node/createView.js new file mode 100644 index 00000000..7d456d90 --- /dev/null +++ b/lib/node/createView.js @@ -0,0 +1,13 @@ +'use strict'; + +var Node = require('./index'); + +module.exports = Node.define({ + type: 'CREATE VIEW', + + constructor: function(viewName) { + Node.call(this); + + this.options = { viewName: viewName}; + } +}); diff --git a/lib/node/default.js b/lib/node/default.js index 49bd7ebd..b5adc894 100644 --- a/lib/node/default.js +++ b/lib/node/default.js @@ -1,6 +1,6 @@ 'use strict'; -module.exports = require(__dirname).define({ +module.exports = require('./index').define({ type: 'DEFAULT', value: function() { return; diff --git a/lib/node/delete.js b/lib/node/delete.js index 2acb5872..0d02ebab 100644 --- a/lib/node/delete.js +++ b/lib/node/delete.js @@ -1,6 +1,6 @@ 'use strict'; -var Node = require(__dirname); +var Node = require('./index'); module.exports = Node.define({ type: 'DELETE' diff --git a/lib/node/distinct.js b/lib/node/distinct.js new file mode 100644 index 00000000..8c681551 --- /dev/null +++ b/lib/node/distinct.js @@ -0,0 +1,7 @@ +'use strict'; + +var Node = require('./index'); + +module.exports = Node.define({ + type: 'DISTINCT', +}); diff --git a/lib/node/distinctOn.js b/lib/node/distinctOn.js new file mode 100644 index 00000000..72c430e8 --- /dev/null +++ b/lib/node/distinctOn.js @@ -0,0 +1,7 @@ +'use strict'; + +var Node = require('./index'); + +module.exports = Node.define({ + type: 'DISTINCT ON', +}); diff --git a/lib/node/drop.js b/lib/node/drop.js index 24e19062..b0428347 100644 --- a/lib/node/drop.js +++ b/lib/node/drop.js @@ -1,7 +1,12 @@ 'use strict'; -var Node = require(__dirname); +var Node = require('./index'); module.exports = Node.define({ - type: 'DROP' + type: 'DROP', + + constructor: function(table) { + Node.call(this); + this.add(table); + } }); diff --git a/lib/node/dropColumn.js b/lib/node/dropColumn.js index 60ad4219..01fe36c5 100644 --- a/lib/node/dropColumn.js +++ b/lib/node/dropColumn.js @@ -1,6 +1,6 @@ 'use strict'; -var Node = require(__dirname); +var Node = require('./index'); module.exports = Node.define({ type: 'DROP COLUMN' diff --git a/lib/node/dropIndex.js b/lib/node/dropIndex.js index 3b09f517..9b5e020d 100644 --- a/lib/node/dropIndex.js +++ b/lib/node/dropIndex.js @@ -1,13 +1,11 @@ 'use strict'; -var Node = require(__dirname); +var Node = require('./'); module.exports = Node.define({ type: 'DROP INDEX', constructor: function(table, indexName) { - var args = Array.prototype.slice.call(arguments); - if (!indexName) { throw new Error('No index defined!'); } else if (Array.isArray(indexName) && (typeof indexName[0] === 'string')) { @@ -19,11 +17,8 @@ module.exports = Node.define({ Node.call(this); - this.table = table; - this.names = []; - this.columns = []; - this.valueSets = []; - this.options = { indexName: indexName }; + this.table = table; + this.options = { indexName: indexName }; }, indexName: function() { diff --git a/lib/node/forShare.js b/lib/node/forShare.js new file mode 100644 index 00000000..68a83678 --- /dev/null +++ b/lib/node/forShare.js @@ -0,0 +1,7 @@ +'use strict'; + +var Node = require('./index'); + +module.exports = Node.define({ + type: 'FOR SHARE' +}); diff --git a/lib/node/forUpdate.js b/lib/node/forUpdate.js new file mode 100644 index 00000000..da4a2518 --- /dev/null +++ b/lib/node/forUpdate.js @@ -0,0 +1,7 @@ +'use strict'; + +var Node = require('./index'); + +module.exports = Node.define({ + type: 'FOR UPDATE' +}); diff --git a/lib/node/foreignKey.js b/lib/node/foreignKey.js new file mode 100644 index 00000000..a0ad99c1 --- /dev/null +++ b/lib/node/foreignKey.js @@ -0,0 +1,19 @@ +'use strict'; + +var Node = require('./index'); + +module.exports = Node.define({ + type: 'FOREIGN KEY', + constructor: function(config) { + Node.call(this); + this.name = config.name; + this.columns = config.columns; + this.schema = config.schema; + this.table = config.table; + this.refColumns = config.refColumns; + this.onUpdate = config.onUpdate; + this.onDelete = config.onDelete; + this.constraint = config.constraint; + } +}); + diff --git a/lib/node/from.js b/lib/node/from.js index 84a6010f..12b54565 100644 --- a/lib/node/from.js +++ b/lib/node/from.js @@ -1,6 +1,6 @@ 'use strict'; -var Node = require(__dirname); +var Node = require('./index'); var From = Node.define({ type: 'FROM' diff --git a/lib/node/functionCall.js b/lib/node/functionCall.js index 6d43c279..608a3c1e 100644 --- a/lib/node/functionCall.js +++ b/lib/node/functionCall.js @@ -1,9 +1,9 @@ 'use strict'; var _ = require('lodash'); -var Node = require(__dirname); -var ParameterNode = require(__dirname + '/parameter'); -var valueExpressionMixin = require(__dirname + '/valueExpression'); +var Node = require('./index'); +var ParameterNode = require('./parameter'); +var valueExpressionMixin = require('./valueExpression'); var FunctionCallNode = Node.define({ type: 'FUNCTION CALL', @@ -18,7 +18,7 @@ var FunctionCallNode = Node.define({ _.extend(FunctionCallNode.prototype, valueExpressionMixin()); // allow aliasing -var AliasNode = require(__dirname + '/alias'); +var AliasNode = require('./alias'); _.extend(FunctionCallNode.prototype, AliasNode.AliasMixin); module.exports = FunctionCallNode; diff --git a/lib/node/groupBy.js b/lib/node/groupBy.js index 6e5888d1..53f63e94 100644 --- a/lib/node/groupBy.js +++ b/lib/node/groupBy.js @@ -1,6 +1,6 @@ 'use strict'; -var Node = require(__dirname); +var Node = require('./index'); module.exports = Node.define({ type: 'GROUP BY' diff --git a/lib/node/having.js b/lib/node/having.js index 7dcf3434..6f1523ac 100644 --- a/lib/node/having.js +++ b/lib/node/having.js @@ -1,6 +1,6 @@ 'use strict'; -var Node = require(__dirname); +var Node = require('./index'); module.exports = Node.define({ type: 'HAVING' diff --git a/lib/node/ifExists.js b/lib/node/ifExists.js index 9b759779..c26df660 100644 --- a/lib/node/ifExists.js +++ b/lib/node/ifExists.js @@ -1,6 +1,6 @@ 'use strict'; -var Node = require(__dirname); +var Node = require('./index'); module.exports = Node.define({ type: 'IF EXISTS' diff --git a/lib/node/ifNotExists.js b/lib/node/ifNotExists.js index ef414551..d731ccb2 100644 --- a/lib/node/ifNotExists.js +++ b/lib/node/ifNotExists.js @@ -1,6 +1,6 @@ 'use strict'; -var Node = require(__dirname); +var Node = require('./index'); module.exports = Node.define({ type: 'IF NOT EXISTS' diff --git a/lib/node/in.js b/lib/node/in.js new file mode 100644 index 00000000..233b7c28 --- /dev/null +++ b/lib/node/in.js @@ -0,0 +1,28 @@ +'use strict'; + +var _ = require('lodash'); +var Node = require('./index'); +var valueExpressionMixin = require('./valueExpression'); + +var valueExpressionMixed = false; +var InNode = Node.define(_.extend({ + type: 'IN', + constructor: function(config) { + Node.call(this); + this.left = config.left; + this.right = config.right; + + // Delay mixin to runtime, when all nodes have been defined, and + // mixin only once. ValueExpressionMixin has circular dependencies. + if (!valueExpressionMixed) { + valueExpressionMixed = true; + _.extend(InNode.prototype, valueExpressionMixin()); + } + }, +})); + +// allow aliasing +var AliasNode = require('./alias'); +_.extend(InNode.prototype, AliasNode.AliasMixin); + +module.exports = InNode; diff --git a/lib/node/index.js b/lib/node/index.js index 7112d9be..66c0e481 100644 --- a/lib/node/index.js +++ b/lib/node/index.js @@ -1,12 +1,11 @@ 'use strict'; -var _ = require('lodash'); var assert = require('assert'); var getDialect = require('../dialect'); var util = require('util'); -/* jshint unused: false */ var Node = function(type) { + /* jshint unused: false */ this.nodes = []; }; @@ -16,7 +15,17 @@ Node.prototype.toNode = function() { Node.prototype.add = function(node) { assert(node, 'Error while trying to add a non-existant node to a query'); - this.nodes.push(typeof node === 'string' ? new TextNode(node) : node.toNode()); + var newNode; + if (typeof node === 'string') newNode = new TextNode(node); + else if (node.toNode) newNode = node.toNode(); + else newNode = node; + this.nodes.push(newNode); + return this; +}; + +Node.prototype.unshift = function(node) { + assert(node, 'Error while trying to add a non-existant node to a query'); + this.nodes.unshift(typeof node === 'string' ? new TextNode(node) : node.toNode()); return this; }; @@ -40,14 +49,28 @@ var determineDialect = function(query, dialect) { return Dialect; }; +var initializeDialect = function(Dialect, query) { + var config = query.sql ? query.sql.config : {}; + return new Dialect(config); +}; + Node.prototype.toQuery = function(dialect) { var Dialect = determineDialect(this, dialect); - return new Dialect().getQuery(this); + return initializeDialect(Dialect, this).getQuery(this); +}; + +Node.prototype.toNamedQuery = function(name, dialect) { + if (!name || typeof name !== 'string' || name === '') { + throw new Error('A query name has to be a non-empty String.'); + } + var query = this.toQuery(dialect); + query.name = name; + return query; }; Node.prototype.toString = function(dialect) { var Dialect = determineDialect(this, dialect); - return new Dialect().getString(this); + return initializeDialect(Dialect, this).getString(this); }; Node.prototype.addAll = function(nodes) { @@ -62,15 +85,17 @@ Node.define = function(def) { Node.call(this); }; // allow custom sub-class constructor - if(def.constructor && def.constructor != {}.constructor) { + if(def.constructor && def.constructor !== {}.constructor) { c = def.constructor; } util.inherits(c, Node); - for(var key in def) { - c.prototype[key] = def[key]; + for (var key in def) { + if (def.hasOwnProperty(key)) { + c.prototype[key] = def[key]; + } } return c; }; module.exports = Node; -var TextNode = require(__dirname + '/text'); +var TextNode = require('./text'); diff --git a/lib/node/indexes.js b/lib/node/indexes.js index 7777c8f7..b1614a9a 100644 --- a/lib/node/indexes.js +++ b/lib/node/indexes.js @@ -1,15 +1,15 @@ 'use strict'; -var Node = require(__dirname); -var IndexesNode = module.exports = Node.define({ +var Node = require('./'); + +var IndexesNode = Node.define({ type: 'INDEXES', constructor: function(table) { Node.call(this); - this.table = table; - this.names = []; - this.columns = []; - this.valueSets = []; + this.table = table; } }); + +module.exports = IndexesNode; diff --git a/lib/node/insert.js b/lib/node/insert.js index 07156d8c..9cd12fbe 100644 --- a/lib/node/insert.js +++ b/lib/node/insert.js @@ -1,8 +1,8 @@ 'use strict'; -var Node = require(__dirname); +var DefaultNode = require('./default'); +var Node = require('./'); var ParameterNode = require('./parameter'); -var DefaultNode = require('./default'); var Insert = Node.define({ type: 'INSERT', @@ -17,6 +17,8 @@ var Insert = Node.define({ module.exports = Insert; Insert.prototype.add = function (nodes) { + var hasColumns = false; + var hasValues = false; var self = this; var values = {}; nodes.forEach(function (node) { @@ -27,14 +29,24 @@ Insert.prototype.add = function (nodes) { self.names.push(name); self.columns.push(column); } + hasColumns = true; + hasValues = hasValues || column.value !== undefined; values[name] = column; }); - this.valueSets.push(values); + + // When none of the columns have a value, it's ambiguous whether the user + // intends to insert a row of default values or append a SELECT statement + // later. Resolve the ambiguity by assuming that if no columns are specified + // it is a row of default values, otherwise a SELECT will be added. + if (hasValues || !hasColumns) { + this.valueSets.push(values); + } + return self; }; /* - * Get paramters for all values to be inserted. This function + * Get parameters for all values to be inserted. This function * handles handles bulk inserts, where keys may be present * in some objects and not others. When keys are not present, * the insert should refer to the column value as DEFAULT. @@ -47,7 +59,7 @@ Insert.prototype.getParameters = function () { self.names.forEach(function (name) { var node = nodeDict[name]; if (node) { - set.push(new ParameterNode(node.value)); + set.push(ParameterNode.getNodeOrParameterNode(node.value)); } else { set.push(new DefaultNode()); diff --git a/lib/node/interval.js b/lib/node/interval.js new file mode 100644 index 00000000..9f46f9ab --- /dev/null +++ b/lib/node/interval.js @@ -0,0 +1,21 @@ +'use strict'; + +var Node = require('./index'); +var ParameterNode = require('./parameter'); + +var IntervalNode = Node.define({ + type: 'INTERVAL', + constructor: function(args) { + Node.call(this); + var interval = args[0] || {}; + this.years = interval.years; + this.months = interval.months; + this.days = interval.days; + this.hours = interval.hours; + this.minutes = interval.minutes; + this.seconds = interval.seconds; + } +}); + +module.exports = IntervalNode; + diff --git a/lib/node/join.js b/lib/node/join.js index a16067e6..f569997e 100644 --- a/lib/node/join.js +++ b/lib/node/join.js @@ -1,10 +1,11 @@ 'use strict'; -var Node = require(__dirname); +var Node = require('./index'); var JoinNode = module.exports = Node.define({ type: 'JOIN', constructor: function(subType, from, to) { Node.call(this); + this.sql = (from.table && from.table.sql) || (to.table && to.table.sql); this.subType = subType; this.from = from.toNode(); this.to = to.toNode(); @@ -18,5 +19,8 @@ var JoinNode = module.exports = Node.define({ }, leftJoin: function(other) { return new JoinNode('LEFT', this, other); + }, + leftJoinLateral: function(other) { + return new JoinNode('LEFT LATERAL', this, other); } }); diff --git a/lib/node/literal.js b/lib/node/literal.js new file mode 100644 index 00000000..f5a1ea58 --- /dev/null +++ b/lib/node/literal.js @@ -0,0 +1,16 @@ +'use strict'; + +var Node = require('./index'); + +module.exports = Node.define({ + type: 'LITERAL', + constructor: function(literal) { + Node.call(this); + this.literal = literal; + this.alias = null; + }, + as: function(alias) { + this.alias = alias; + return this; + } +}); diff --git a/lib/node/notIn.js b/lib/node/notIn.js new file mode 100644 index 00000000..c50976c3 --- /dev/null +++ b/lib/node/notIn.js @@ -0,0 +1,28 @@ +'use strict'; + +var _ = require('lodash'); +var Node = require('./index'); +var valueExpressionMixin = require('./valueExpression'); + +var valueExpressionMixed = false; +var NotInNode = Node.define(_.extend({ + type: 'NOT IN', + constructor: function(config) { + Node.call(this); + this.left = config.left; + this.right = config.right; + + // Delay mixin to runtime, when all nodes have been defined, and + // mixin only once. ValueExpressionMixin has circular dependencies. + if (!valueExpressionMixed) { + valueExpressionMixed = true; + _.extend(NotInNode.prototype, valueExpressionMixin()); + } + }, +})); + +// allow aliasing +var AliasNode = require('./alias'); +_.extend(NotInNode.prototype, AliasNode.AliasMixin); + +module.exports = NotInNode; diff --git a/lib/node/onConflict.js b/lib/node/onConflict.js new file mode 100644 index 00000000..cc71ac47 --- /dev/null +++ b/lib/node/onConflict.js @@ -0,0 +1,7 @@ +'use strict'; + +var Node = require('./index'); + +module.exports = Node.define({ + type: 'ONCONFLICT' +}); diff --git a/lib/node/onDuplicate.js b/lib/node/onDuplicate.js new file mode 100644 index 00000000..cd653cb3 --- /dev/null +++ b/lib/node/onDuplicate.js @@ -0,0 +1,7 @@ +'use strict'; + +var Node = require('./index'); + +module.exports = Node.define({ + type: 'ONDUPLICATE' +}); diff --git a/lib/node/orIgnore.js b/lib/node/orIgnore.js new file mode 100644 index 00000000..e23c9d6d --- /dev/null +++ b/lib/node/orIgnore.js @@ -0,0 +1,7 @@ +'use strict'; + +var Node = require('./index'); + +module.exports = Node.define({ + type: 'OR IGNORE' +}); diff --git a/lib/node/orderBy.js b/lib/node/orderBy.js index 6db2a978..b97fb891 100644 --- a/lib/node/orderBy.js +++ b/lib/node/orderBy.js @@ -1,6 +1,6 @@ 'use strict'; -var Node = require(__dirname); +var Node = require('./index'); module.exports = Node.define({ type: 'ORDER BY' diff --git a/lib/node/orderByColumn.js b/lib/node/orderByColumn.js deleted file mode 100644 index 376abc3f..00000000 --- a/lib/node/orderByColumn.js +++ /dev/null @@ -1,11 +0,0 @@ -'use strict'; - -var Node = require(__dirname); -var OrderByColumn = module.exports = Node.define({ - type: 'ORDER BY COLUMN', - constructor: function(config) { - Node.call(this); - this.column = config.column; - this.direction = config.direction; - } -}); diff --git a/lib/node/orderByValue.js b/lib/node/orderByValue.js new file mode 100644 index 00000000..47a8d5c0 --- /dev/null +++ b/lib/node/orderByValue.js @@ -0,0 +1,17 @@ +'use strict'; + +var Node = require('./index'); + +var OrderByColumn = Node.define({ + type: 'ORDER BY VALUE', + constructor: function(config) { + Node.call(this); + this.value = config.value; + this.direction = config.direction; + // used when processing OFFSET and LIMIT clauses in MSSQL + this.msSQLOffsetNode=undefined; + this.msSQLLimitNode=undefined; + } +}); + + module.exports = OrderByColumn; diff --git a/lib/node/parameter.js b/lib/node/parameter.js index 7b3a8a20..051ed852 100644 --- a/lib/node/parameter.js +++ b/lib/node/parameter.js @@ -1,12 +1,13 @@ 'use strict'; -var Node = require(__dirname); +var Node = require('./index'); var ParameterNode = module.exports = Node.define({ type: 'PARAMETER', constructor: function(val) { Node.call(this); this._val = val; + this.isExplicit = false; }, value: function() { return this._val; diff --git a/lib/node/postfixUnary.js b/lib/node/postfixUnary.js index 0c132f8a..8c66b89f 100644 --- a/lib/node/postfixUnary.js +++ b/lib/node/postfixUnary.js @@ -1,8 +1,8 @@ 'use strict'; var _ = require('lodash'); -var Node = require(__dirname); -var valueExpressionMixin = require(__dirname + '/valueExpression'); +var Node = require('./index'); +var valueExpressionMixin = require('./valueExpression'); var valueExpressionMixed = false; var PostfixUnaryNode = Node.define({ @@ -22,7 +22,7 @@ var PostfixUnaryNode = Node.define({ }); // allow aliasing -var AliasNode = require(__dirname + '/alias'); +var AliasNode = require('./alias'); _.extend(PostfixUnaryNode.prototype, AliasNode.AliasMixin); module.exports = PostfixUnaryNode; diff --git a/lib/node/prefixUnary.js b/lib/node/prefixUnary.js index 67a59fa1..fbde69c0 100644 --- a/lib/node/prefixUnary.js +++ b/lib/node/prefixUnary.js @@ -1,8 +1,8 @@ 'use strict'; var _ = require('lodash'); -var Node = require(__dirname); -var valueExpressionMixin = require(__dirname + '/valueExpression'); +var Node = require('./index'); +var valueExpressionMixin = require('./valueExpression'); var valueExpressionMixed = false; var PrefixUnaryNode = Node.define({ @@ -22,7 +22,7 @@ var PrefixUnaryNode = Node.define({ }); // allow aliasing -var AliasNode = require(__dirname + '/alias'); +var AliasNode = require('./alias'); _.extend(PrefixUnaryNode.prototype, AliasNode.AliasMixin); module.exports = PrefixUnaryNode; diff --git a/lib/node/query.js b/lib/node/query.js index 0d638532..ebb3ecfa 100644 --- a/lib/node/query.js +++ b/lib/node/query.js @@ -1,36 +1,52 @@ 'use strict'; +var _ = require('lodash'); +var alias = require('./alias'); var assert = require('assert'); -var util = require('util'); - var sliced = require('sliced'); +var util = require('util'); +var valueExpressionMixin = require('./valueExpression'); -var Node = require(__dirname); -var Select = require(__dirname + '/select'); -var From = require(__dirname + '/from'); -var Where = require(__dirname + '/where'); -var OrderBy = require(__dirname + '/orderBy'); -var GroupBy = require(__dirname + '/groupBy'); -var Having = require(__dirname + '/having'); -var Insert = require(__dirname + '/insert'); -var Update = require(__dirname + '/update'); -var Delete = require(__dirname + '/delete'); -var Returning = require(__dirname + '/returning'); -var Create = require(__dirname + '/create'); -var Drop = require(__dirname + '/drop'); -var Alter = require(__dirname + '/alter'); -var AddColumn = require(__dirname + '/addColumn'); -var DropColumn = require(__dirname + '/dropColumn'); -var RenameColumn = require(__dirname + '/renameColumn'); -var Rename = require(__dirname + '/rename'); -var Column = require(__dirname + '/../column'); -var ParameterNode = require(__dirname + '/parameter'); -var PrefixUnaryNode = require(__dirname + '/prefixUnary'); -var IfExists = require(__dirname + '/ifExists'); -var IfNotExists = require(__dirname + '/ifNotExists'); -var Indexes = require(__dirname + '/indexes'); -var CreateIndex = require(__dirname + '/createIndex'); -var DropIndex = require(__dirname + '/dropIndex'); +var Node = require('./'); +var Select = require('./select'); +var From = require('./from'); +var Where = require('./where'); +var OrderBy = require('./orderBy'); +var GroupBy = require('./groupBy'); +var Having = require('./having'); +var Insert = require('./insert'); +var Replace = require('./replace'); +var Update = require('./update'); +var Delete = require('./delete'); +var Returning = require('./returning'); +var OnDuplicate = require('./onDuplicate'); +var OnConflict = require('./onConflict'); +var ForUpdate = require('./forUpdate'); +var ForShare = require('./forShare'); +var Create = require('./create'); +var Drop = require('./drop'); +var Truncate = require('./truncate'); +var Distinct = require('./distinct'); +var DistinctOn = require('./distinctOn'); +var Alter = require('./alter'); +var AddColumn = require('./addColumn'); +var DropColumn = require('./dropColumn'); +var RenameColumn = require('./renameColumn'); +var Rename = require('./rename'); +var Column = require('../column'); +var ParameterNode = require('./parameter'); +var PrefixUnaryNode = require('./prefixUnary'); +var IfExists = require('./ifExists'); +var IfNotExists = require('./ifNotExists'); +var OrIgnore = require('./orIgnore'); +var Cascade = require('./cascade'); +var Restrict = require('./restrict'); +var Indexes = require('./indexes'); +var CreateIndex = require('./createIndex'); +var DropIndex = require('./dropIndex'); +var Table = require('./table'); +var CreateView = require('./createView'); +var JoinNode = require('./join'); var Modifier = Node.define({ constructor: function(table, type, count) { @@ -40,109 +56,162 @@ var Modifier = Node.define({ } }); +// get the first element of an arguments if it is an array, else return arguments as an array +var getArrayOrArgsAsArray = function(args) { + if (util.isArray(args[0])) { + return args[0]; + } + return sliced(args); +}; + var Query = Node.define({ type: 'QUERY', + constructor: function(table) { Node.call(this); this.table = table; - if (table) this.sql = table.sql; + if (table) { + this.sql = table.sql; + } }, + select: function() { var select; - if(this._select) { + if (this._select) { select = this._select; } else { select = this._select = new Select(); this.add(select); } - var args = sliced(arguments); - if(util.isArray(args[0])) { - args = args[0]; - } + + //allow things like .select(a.star(), [ a.id, a.name ]) + //this will flatten them into a single array + var args = sliced(arguments).reduce(function(cur, next) { + if (util.isArray(next)) { + return cur.concat(next); + } + + cur.push(next); + return cur; + }, []); + select.addAll(args); + // if this is a subquery then add reference to this column - if(this.type === 'SUBQUERY') { - for(var j = 0; j < select.nodes.length; j++) { + if (this.type === 'SUBQUERY') { + for (var j = 0; j < select.nodes.length; j++) { var name = select.nodes[j].alias || select.nodes[j].name; var col = new Column(select.nodes[j]); col.name = name; + col.property = name; col.table = this; - if( this[name] === undefined ) { + if (this[name] === undefined) { this[name] = col; } } } return this; }, + star: function() { - assert( this.type === 'SUBQUERY', 'star() can only be used on a subQuery'); - return new Column({table: this, star: true}); + assert(this.type === 'SUBQUERY', 'star() can only be used on a subQuery'); + return new Column({ + table: this, + star: true + }); }, - from: function(tableNode) { - var from = new From().add(tableNode); - return this.add(from); + + from: function() { + var tableNodes = arguments; + + if (Array.isArray(arguments[0])) { + tableNodes = arguments[0]; + } + + for (var i=0; i 1) { + if (arguments.length > 1) { // allow multiple where clause arguments var args = sliced(arguments); - for(var i = 0; i < args.length; i++) { + for (var i = 0; i < args.length; i++) { this.where(args[i]); } return this; } // calling #where twice functions like calling #where & then #and - if(this.whereClause) return this.and(node); + if (this.whereClause) { + return this.and(node); + } this.whereClause = new Where(this.table); this.whereClause.add(node); return this.add(this.whereClause); }, + or: function(node) { + if (!this.whereClause) return this.where(node); this.whereClause.or(node); return this; }, + and: function(node) { + if (!this.whereClause) return this.where(node); this.whereClause.and(node); return this; }, + order: function() { - var args = sliced(arguments); - if(util.isArray(args[0])) { - args = args[0]; + var args = getArrayOrArgsAsArray(arguments); + var orderBy; + if (args.length === 0) { + return this; } - if (!this.orderBy) { - this.add(this.orderBy = new OrderBy()); + if (this._orderBy) { + orderBy = this._orderBy; + } else { + orderBy = this._orderBy = new OrderBy(); + this.add(orderBy); } - this.orderBy.addAll(args); + orderBy.addAll(args); return this; }, + group: function() { - var args = sliced(arguments); - if(util.isArray(args[0])) { - args = args[0]; - } + var args = getArrayOrArgsAsArray(arguments); var groupBy = new GroupBy().addAll(args); return this.add(groupBy); }, + having: function() { - var args = sliced(arguments); - if (util.isArray(args[0])) { - args = args[0]; - } + var args = getArrayOrArgsAsArray(arguments); var having = new Having().addAll(args); return this.add(having); }, + insert: function(o) { var self = this; var args = sliced(arguments); // object literal - if(arguments.length == 1 && !o.toNode && !o.forEach) { - args = Object.keys(o).map(function(key) { - return self.table.get(key).value(o[key]); + if (arguments.length === 1 && !o.toNode && !o.forEach) { + args = []; + Object.keys(o).forEach(function(key) { + var col = self.table.get(key); + if(col && !col.autoGenerated) + args.push(col.value(o[key])); }); } else if (o.forEach) { - o.forEach(function (arg) { + o.forEach(function(arg) { return self.insert.call(self, arg); }); return self; @@ -151,87 +220,230 @@ var Query = Node.define({ if (self.insertClause) { self.insertClause.add(args); return self; - } - else { + } else { self.insertClause = new Insert(); self.insertClause.add(args); return self.add(self.insertClause); } }, + + replace: function(o) { + var self = this; + + var args = sliced(arguments); + // object literal + if (arguments.length === 1 && !o.toNode && !o.forEach) { + args = []; + Object.keys(o).forEach(function(key) { + var col = self.table.get(key); + if(col && !col.autoGenerated) + args.push(col.value(o[key])); + }); + } else if (o.forEach) { + o.forEach(function(arg) { + return self.replace.call(self, arg); + }); + return self; + } + + if (self.replaceClause) { + self.replaceClause.add(args); + return self; + } else { + self.replaceClause = new Replace(); + self.replaceClause.add(args); + return self.add(self.replaceClause); + } + + }, + update: function(o) { var self = this; var update = new Update(); Object.keys(o).forEach(function(key) { - var val = o[key]; - update.add(self.table.get(key).value(ParameterNode.getNodeOrParameterNode(val))); + var col = self.table.get(key); + if(col && !col.autoGenerated) { + var val = o[key]; + update.add(col.value(ParameterNode.getNodeOrParameterNode(val))); + } }); return this.add(update); }, + + parameter: function(v) { + var param = ParameterNode.getNodeOrParameterNode(v); + param.isExplicit = true; + return this.add(param); + }, + delete: function(params) { - var result = this.add(new Delete()); - if(params) { - result = this.where(params); + var result; + if (params) { + var TableDefinition = require('../table'); + if (params instanceof TableDefinition || Array.isArray(params)) { + //handle explicit delete queries: + // e.g. post.delete(post).from(post) -> DELETE post FROM post + // e.g. post.delete([post, user]).from(post) -> DELETE post, user FROM post + if (Array.isArray(params)) { + params = params.map(function(table) { return new Table(table); }); + } else { + params = [ new Table(params) ]; + } + result = this.add(new Delete().addAll(params)); + } else { + //syntax sugar for post.delete().from(post).where(params) + result = this.add(new Delete()).where(params); + } + } else{ + result = this.add(new Delete()); } return result; }, + returning: function() { var returning = new Returning(); - var args = sliced(arguments); - if(util.isArray(args[0])) { - args = args[0]; - } - returning.addAll(args); + if (arguments.length === 0) + returning.add('*'); + else + returning.addAll(getArrayOrArgsAsArray(arguments)); + return this.add(returning); }, + onDuplicate: function(o) { + var self = this; + + var onDuplicate = new OnDuplicate(); + Object.keys(o).forEach(function(key) { + var col = self.table.get(key); + if(col && !col.autoGenerated) + var val = o[key]; + onDuplicate.add(col.value(ParameterNode.getNodeOrParameterNode(val))); // jshint ignore:line + }); + + return self.add(onDuplicate); + }, + + onConflict: function(o) { + var self = this; + + var onConflict = new OnConflict(); + Object.keys(o).forEach(function(key) { + onConflict[key] = o[key]; + }); + return self.add(onConflict); + }, + + forUpdate: function() { + assert(typeof this._select !== 'undefined', 'FOR UPDATE can be used only in a select statement'); + this.add(new ForUpdate()); + return this; + }, + + forShare: function() { + assert(typeof this._select !== 'undefined', 'FOR SHARE can be used only in a select statement'); + this.add(new ForShare()); + return this; + }, + create: function(indexName) { - if (this.indexesCause) { + if (this.indexesClause) { var createIndex = new CreateIndex(this.table, indexName); this.add(createIndex); return createIndex; } else { - return this.add(new Create()); + return this.add(new Create(this.table.isTemporary)); } }, drop: function() { - if (this.indexesCause) { - var args = Array.prototype.slice.call(arguments); + if (this.indexesClause) { + var args = sliced(arguments); var dropIndex = new DropIndex(this.table, args); this.add(dropIndex); return dropIndex; } else { - return this.add(new Drop()); + return this.add(new Drop(this.table)); } }, + truncate: function() { + return this.add(new Truncate(this.table)); + }, + + distinct: function() { + return this.add(new Distinct()); + }, + + distinctOn: function() { + var distinctOn; + if (this._distinctOn) { + distinctOn = this._distinctOn; + } else { + var select = this.nodes.filter(function (node) {return node.type === 'SELECT';}).shift(); + + distinctOn = this._distinctOn = new DistinctOn(); + select.add(distinctOn); + } + + //allow things like .distinctOn(a.star(), [ a.id, a.name ]) + //this will flatten them into a single array + var args = sliced(arguments).reduce(function(cur, next) { + if (util.isArray(next)) { + return cur.concat(next); + } + + cur.push(next); + return cur; + }, []); + + distinctOn.addAll(args); + + return this; + }, + alter: function() { return this.add(new Alter()); }, rename: function(newName) { var renameClause = new Rename(); - if (!newName.toNode) - newName = new Column({ name: newName, table: this.table}); + if (!newName.toNode) { + newName = new Column({ + name: newName, + table: this.table + }); + } renameClause.add(newName.toNode()); this.nodes[0].add(renameClause); return this; }, + addColumn: function(column, dataType) { var addClause = new AddColumn(); - if (!column.toNode) - column = new Column({name: column, table: this.table}); - if (dataType) + if (!column.toNode) { + column = new Column({ + name: column, + table: this.table + }); + } + if (dataType) { column.dataType = dataType; + } addClause.add(column.toNode()); this.nodes[0].add(addClause); return this; }, + dropColumn: function(column) { var dropClause = new DropColumn(); - if (!column.toNode) - column = new Column({name: column, table: this.table}); + if (!column.toNode) { + column = new Column({ + name: column, + table: this.table + }); + } dropClause.add(column.toNode()); this.nodes[0].add(dropClause); return this; @@ -239,10 +451,18 @@ var Query = Node.define({ renameColumn: function(oldColumn, newColumn) { var renameClause = new RenameColumn(); - if (!oldColumn.toNode) - oldColumn = new Column({name: oldColumn, table: this.table}); - if (!newColumn.toNode) - newColumn = new Column({name: newColumn, table: this.table}); + if (!oldColumn.toNode) { + oldColumn = new Column({ + name: oldColumn, + table: this.table + }); + } + if (!newColumn.toNode) { + newColumn = new Column({ + name: newColumn, + table: this.table + }); + } renameClause.add(oldColumn.toNode()); renameClause.add(newColumn.toNode()); this.nodes[0].add(renameClause); @@ -274,19 +494,59 @@ var Query = Node.define({ }, ifExists: function() { - this.nodes[0].add(new IfExists()); + this.nodes[0].unshift(new IfExists()); return this; }, ifNotExists: function() { - this.nodes[0].add(new IfNotExists()); + this.nodes[0].unshift(new IfNotExists()); + return this; + }, + + orIgnore: function() { + this.nodes[0].unshift(new OrIgnore()); + return this; + }, + + cascade: function() { + this.nodes[0].add(new Cascade()); + return this; + }, + + restrict: function() { + this.nodes[0].add(new Restrict()); return this; }, indexes: function() { - this.indexesCause = new Indexes({ table: this.table }); - return this.add(this.indexesCause); + this.indexesClause = new Indexes({ + table: this.table + }); + return this.add(this.indexesClause); + }, + + createView: function(viewName) { + this.add(new CreateView(viewName)); + return this; } }); +// Here we are extending query with valueExpressions so that it's possible to write queries like +// var query=sql.select(a.select(a.x.sum()).plus(b.select(b.y.sum())) +// which generates: +// SELECT (SELECT SUM(a.x) FROM a) + (SELECT SUM(b.y) FROM b) +// We need to remove "or" and "and" from here because it conflicts with the already existing functionality of appending +// to the where clause like so: +// var query=a.select().where(a.name.equals("joe")).or(a.name.equals("sam")) +var valueExpressions=valueExpressionMixin(); +delete valueExpressions.or; +delete valueExpressions.and; +_.extend(Query.prototype, valueExpressions); + +// Extend the query with the aliasMixin so that it's possible to write queries like +// var query=sql.select(a.select(a.count()).as("column1")) +// which generates: +// SELECT (SELECT COUNT(*) FROM a) AS "column1" +_.extend(Query.prototype, alias.AliasMixin); + module.exports = Query; diff --git a/lib/node/rename.js b/lib/node/rename.js index 9767a528..c87bcfb3 100644 --- a/lib/node/rename.js +++ b/lib/node/rename.js @@ -1,6 +1,6 @@ 'use strict'; -var Node = require(__dirname); +var Node = require('./index'); module.exports = Node.define({ type: 'RENAME' diff --git a/lib/node/renameColumn.js b/lib/node/renameColumn.js index f886a11d..c3c66865 100644 --- a/lib/node/renameColumn.js +++ b/lib/node/renameColumn.js @@ -1,6 +1,6 @@ 'use strict'; -var Node = require(__dirname); +var Node = require('./index'); module.exports = Node.define({ type: 'RENAME COLUMN' diff --git a/lib/node/replace.js b/lib/node/replace.js new file mode 100644 index 00000000..d17099d2 --- /dev/null +++ b/lib/node/replace.js @@ -0,0 +1,70 @@ +'use strict'; + +var DefaultNode = require('./default'); +var Node = require('./'); +var ParameterNode = require('./parameter'); + +var Replace = Node.define({ + type: 'REPLACE', + constructor: function () { + Node.call(this); + this.names = []; + this.columns = []; + this.valueSets = []; + } +}); + +module.exports = Replace; + +Replace.prototype.add = function (nodes) { + var hasColumns = false; + var hasValues = false; + var self = this; + var values = {}; + nodes.forEach(function (node) { + var column = node.toNode(); + var name = column.name; + var idx = self.names.indexOf(name); + if (idx < 0) { + self.names.push(name); + self.columns.push(column); + } + hasColumns = true; + hasValues = hasValues || column.value !== undefined; + values[name] = column; + }); + + // When none of the columns have a value, it's ambiguous whether the user + // intends to replace a row of default values or append a SELECT statement + // later. Resolve the ambiguity by assuming that if no columns are specified + // it is a row of default values, otherwise a SELECT will be added. + if (hasValues || !hasColumns) { + this.valueSets.push(values); + } + + return self; +}; + +/* + * Get parameters for all values to be replaced. This function + * handles handles bulk replaces, where keys may be present + * in some objects and not others. When keys are not present, + * the replace should refer to the column value as DEFAULT. + */ +Replace.prototype.getParameters = function () { + var self = this; + return this.valueSets + .map(function (nodeDict) { + var set = []; + self.names.forEach(function (name) { + var node = nodeDict[name]; + if (node) { + set.push(ParameterNode.getNodeOrParameterNode(node.value)); + } + else { + set.push(new DefaultNode()); + } + }); + return set; + }); +}; diff --git a/lib/node/restrict.js b/lib/node/restrict.js new file mode 100644 index 00000000..942f4112 --- /dev/null +++ b/lib/node/restrict.js @@ -0,0 +1,7 @@ +'use strict'; + +var Node = require('./index'); + +module.exports = Node.define({ + type: 'RESTRICT' +}); diff --git a/lib/node/returning.js b/lib/node/returning.js index 35ea0a18..2f27d067 100644 --- a/lib/node/returning.js +++ b/lib/node/returning.js @@ -1,6 +1,6 @@ 'use strict'; -var Node = require(__dirname); +var Node = require('./index'); module.exports = Node.define({ type: 'RETURNING' diff --git a/lib/node/select.js b/lib/node/select.js index db3f3230..93e22538 100644 --- a/lib/node/select.js +++ b/lib/node/select.js @@ -1,6 +1,6 @@ 'use strict'; -var Node = require(__dirname); +var Node = require('./index'); module.exports = Node.define({ type: 'SELECT', @@ -9,5 +9,9 @@ module.exports = Node.define({ if (arg && arg.sql) { this.sql = arg.sql; } - } + // used when processing LIMIT clauses in MSSQL + this.msSQLLimitNode = undefined; + // set to true when a DISTINCT is used on the entire result set + this.isDistinct = false; + } }); diff --git a/lib/node/slice.js b/lib/node/slice.js new file mode 100644 index 00000000..86c1ce21 --- /dev/null +++ b/lib/node/slice.js @@ -0,0 +1,28 @@ +'use strict'; + +var _ = require('lodash'); +var Node = require('./index'); +var valueExpressionMixin = require('./valueExpression'); + +var valueExpressionMixed = false; +var SliceNode = Node.define({ + type: 'SLICE', + constructor: function(value, start, end) { + Node.call(this); + this.value = value; + this.start = start; + this.end = end; + // Delay mixin to runtime, when all nodes have been defined, and + // mixin only once. ValueExpressionMixin has circular dependencies. + if (!valueExpressionMixed) { + valueExpressionMixed = true; + _.extend(SliceNode.prototype, valueExpressionMixin()); + } + } +}); + +// allow aliasing +var AliasNode = require('./alias'); +_.extend(SliceNode.prototype, AliasNode.AliasMixin); + +module.exports = SliceNode; diff --git a/lib/node/table.js b/lib/node/table.js index b1257105..4fca70d1 100644 --- a/lib/node/table.js +++ b/lib/node/table.js @@ -1,6 +1,6 @@ 'use strict'; -var Node = require(__dirname); +var Node = require('./index'); module.exports = Node.define({ type: 'TABLE', constructor: function(table) { diff --git a/lib/node/ternary.js b/lib/node/ternary.js index 1ef5ce7b..3f688ba9 100644 --- a/lib/node/ternary.js +++ b/lib/node/ternary.js @@ -1,8 +1,8 @@ 'use strict'; var _ = require('lodash'); -var Node = require(__dirname); -var valueExpressionMixin = require(__dirname + '/valueExpression'); +var Node = require('./index'); +var valueExpressionMixin = require('./valueExpression'); var valueExpressionMixed = false; var TernaryNode = Node.define(_.extend({ @@ -25,7 +25,7 @@ var TernaryNode = Node.define(_.extend({ })); // allow aliasing -var AliasNode = require(__dirname + '/alias'); +var AliasNode = require('./alias'); _.extend(TernaryNode.prototype, AliasNode.AliasMixin); module.exports = TernaryNode; diff --git a/lib/node/text.js b/lib/node/text.js index ef1194b2..bf026151 100644 --- a/lib/node/text.js +++ b/lib/node/text.js @@ -1,6 +1,6 @@ 'use strict'; -var Node = require(__dirname); +var Node = require('./index'); module.exports = Node.define({ type: 'TEXT', diff --git a/lib/node/truncate.js b/lib/node/truncate.js new file mode 100644 index 00000000..165e0d9e --- /dev/null +++ b/lib/node/truncate.js @@ -0,0 +1,12 @@ +'use strict'; + +var Node = require('./index'); + +module.exports = Node.define({ + type: 'TRUNCATE', + + constructor: function(table) { + Node.call(this); + this.add(table); + } +}); diff --git a/lib/node/update.js b/lib/node/update.js index 32a29ba9..890fbee9 100644 --- a/lib/node/update.js +++ b/lib/node/update.js @@ -1,6 +1,6 @@ 'use strict'; -var Node = require(__dirname); +var Node = require('./index'); module.exports = Node.define({ type: 'UPDATE' diff --git a/lib/node/valueExpression.js b/lib/node/valueExpression.js index cc8bad4e..2fa741a9 100644 --- a/lib/node/valueExpression.js +++ b/lib/node/valueExpression.js @@ -1,8 +1,8 @@ 'use strict'; -var _ = require('lodash'); -var Node = require(__dirname); -var ParameterNode = require(__dirname + '/parameter'); +var OrderByValueNode = require('./orderByValue'); +var ParameterNode = require('./parameter'); +var TextNode = require('./text'); // Process values, wrapping them in ParameterNode if necessary. var processParams = function(val) { @@ -12,17 +12,23 @@ var processParams = function(val) { // Value expressions can be composed to form new value expressions. // ValueExpressionMixin is evaluated at runtime, hence the // "thunk" around it. -var ValueExpressionMixin = module.exports = function() { - var PostfixUnaryNode = require(__dirname + '/postfixUnary'); - var BinaryNode = require(__dirname + '/binary'); - var TernaryNode = require(__dirname + '/ternary'); +var ValueExpressionMixin = function() { + var BinaryNode = require('./binary'); + var InNode = require('./in'); + var NotInNode = require('./notIn'); + var CastNode = require('./cast'); + var PostfixUnaryNode = require('./postfixUnary'); + var TernaryNode = require('./ternary'); + var CaseNode = require('./case'); + var AtNode = require('./at'); + var SliceNode = require('./slice'); var postfixUnaryMethod = function(operator) { /*jshint unused: false */ return function(val) { return new PostfixUnaryNode({ - left: this.toNode(), - operator: operator + left : this.toNode(), + operator : operator }); }; }; @@ -30,25 +36,71 @@ var ValueExpressionMixin = module.exports = function() { var binaryMethod = function(operator) { return function(val) { return new BinaryNode({ - left: this.toNode(), - operator: operator, - right: processParams(val) + left : this.toNode(), + operator : operator, + right : processParams(val) }); }; }; + var inMethod = function(val) { + return new InNode({ + left : this.toNode(), + right : processParams(val) + }); + }; + + var notInMethod = function(val) { + return new NotInNode({ + left : this.toNode(), + right : processParams(val) + }); + }; + var ternaryMethod = function(operator, separator) { return function(middle, right) { return new TernaryNode({ - left: this.toNode(), - operator: operator, - middle: processParams(middle), - separator: separator, - right: processParams(right) + left : this.toNode(), + operator : operator, + middle : processParams(middle), + separator : separator, + right : processParams(right) }); }; }; + var atMethod = function(index) { + return new AtNode(this.toNode(), processParams(index)); + }; + + var sliceMethod = function(start, end) { + return new SliceNode(this.toNode(), processParams(start), processParams(end)); + }; + + var castMethod = function(dataType) { + return new CastNode(this.toNode(), dataType); + }; + + var orderMethod = function(direction) { + return function() { + return new OrderByValueNode({ + value : this.toNode(), + direction : direction ? new TextNode(direction) : undefined + }); + }; + }; + + var caseMethod = function(whenList, thenList, elseBranch) { + if (undefined !== elseBranch) { + elseBranch = processParams(elseBranch); + } + return new CaseNode({ + whenList : processParams(whenList), + thenList : processParams(thenList), + else : elseBranch + }); + }; + return { isNull : postfixUnaryMethod('IS NULL'), isNotNull : postfixUnaryMethod('IS NOT NULL'), @@ -69,10 +121,40 @@ var ValueExpressionMixin = module.exports = function() { modulo : binaryMethod('%'), leftShift : binaryMethod('<<'), rightShift : binaryMethod('>>'), + bitwiseAnd : binaryMethod('&'), + bitwiseNot : binaryMethod('~'), + bitwiseOr : binaryMethod('|'), + bitwiseXor : binaryMethod('#'), + regex : binaryMethod('~'), + iregex : binaryMethod('~*'), + regexp : binaryMethod('REGEXP'), + notRegex : binaryMethod('!~'), + notIregex : binaryMethod('!~*'), + concat : binaryMethod('||'), + key : binaryMethod('->'), + keyText : binaryMethod('->>'), + path : binaryMethod('#>'), + pathText : binaryMethod('#>>'), like : binaryMethod('LIKE'), + rlike : binaryMethod('RLIKE'), notLike : binaryMethod('NOT LIKE'), - in : binaryMethod('IN'), - notIn : binaryMethod('NOT IN'), - between : ternaryMethod('BETWEEN', 'AND') + ilike : binaryMethod('ILIKE'), + notIlike : binaryMethod('NOT ILIKE'), + match : binaryMethod('@@'), + in : inMethod, + notIn : notInMethod, + between : ternaryMethod('BETWEEN', 'AND'), + notBetween : ternaryMethod('NOT BETWEEN', 'AND'), + at : atMethod, + contains : binaryMethod('@>'), + containedBy : binaryMethod('<@'), + containsKey : binaryMethod('?'), + overlap : binaryMethod('&&'), + slice : sliceMethod, + cast : castMethod, + descending : orderMethod('DESC'), + case : caseMethod }; }; + +module.exports = ValueExpressionMixin; diff --git a/lib/node/where.js b/lib/node/where.js index 3b22ae9d..5c0465fc 100644 --- a/lib/node/where.js +++ b/lib/node/where.js @@ -1,23 +1,41 @@ 'use strict'; -var Node = require(__dirname); -var BinaryNode = require(__dirname + '/binary'); -var TextNode = require(__dirname + '/text'); +var Node = require('./index'); +var BinaryNode = require('./binary'); +var TextNode = require('./text'); var normalizeNode = function(table, node) { var result = node; - if(typeof node == 'string') { + if(typeof node === 'string') { result = new TextNode('(' + node + ')'); } - else if (!node.toNode && typeof node == 'object'){ + else if (Array.isArray(node)) { + result = false; + + if (node.length === 0) { + result = new TextNode('(1 = 1)'); + } else { + node.forEach(function (subNode) { + if (!result) { + result = subNode; + } else { + result = result.and(subNode); + } + }); + } + } + else if (!node.toNode && typeof node === 'object'){ result = false; for (var colName in node) { - var column = table.getColumn(colName); - var query = column.equals(node[colName]); - if (!result) - result = query; - else - result = result.and(query); + if (node.hasOwnProperty(colName)) { + var column = table.getColumn(colName); + var query = column.equals(node[colName]); + if (!result) { + result = query; + } else { + result = result.and(query); + } + } } } return result; diff --git a/lib/table.js b/lib/table.js index 9bf84ab7..1408542b 100644 --- a/lib/table.js +++ b/lib/table.js @@ -3,32 +3,42 @@ var util = require('util'); var lodash = require('lodash'); -var Query = require(__dirname + '/node/query'); -var Column = require(__dirname + '/column'); -var TableNode = require(__dirname + '/node/table'); -var JoinNode = require(__dirname + '/node/join'); -var Joiner = require(__dirname + '/joiner'); +var Query = require('./node/query'); +var Column = require('./column'); +var TableNode = require('./node/table'); +var JoinNode = require('./node/join'); +var LiteralNode = require('./node/literal'); +var Joiner = require('./joiner'); +var ForeignKeyNode = require('./node/foreignKey'); var Table = function(config) { this._schema = config.schema; this._name = config.name; this._initialConfig = config; + this.columnWhiteList = !!config.columnWhiteList; + this.isTemporary=!!config.isTemporary; + this.snakeToCamel = !!config.snakeToCamel; this.columns = []; + this.foreignKeys = []; this.table = this; - if (!config.sql) config.sql = require('./index'); + if (!config.sql) { + config.sql = require('./index'); + } this.sql = config.sql; }; Table.define = function(config) { var table = new Table(config); // allow hash of columns as well as array - if(config.columns && !util.isArray(config.columns)) { + if (config.columns && !util.isArray(config.columns)) { var cols = []; - for(var key in config.columns) { - var col = config.columns[key]; - col.name = key; - cols.push(col); + for (var key in config.columns) { + if (config.columns.hasOwnProperty(key)) { + var col = config.columns[key]; + col.name = key; + cols.push(col); + } } config.columns = cols; @@ -37,9 +47,31 @@ Table.define = function(config) { for (var i = 0; i < config.columns.length; i++) { table.addColumn(config.columns[i]); } + + if(config.foreignKeys !== undefined) { + if(util.isArray(config.foreignKeys)) { + for(i = 0; i < config.foreignKeys.length; i++) { + table.foreignKeys.push(new ForeignKeyNode(config.foreignKeys[i])); + } + } else { + table.foreignKeys.push(new ForeignKeyNode(config.foreignKeys)); + } + } return table; }; +Table.prototype.clone = function(config) { + return Table.define(lodash.extend({ + schema: this._schema, + name: this._name, + sql: this.sql, + columnWhiteList: !!this.columnWhiteList, + snakeToCamel: !!this.snakeToCamel, + columns: this.columns, + foreignKeys: this.foreignKeys + }, config || {})); +}; + Table.prototype.createColumn = function(col) { if(!(col instanceof Column)) { if(typeof col === 'string') { @@ -48,6 +80,20 @@ Table.prototype.createColumn = function(col) { col.table = this; col = new Column(col); + + // Load subfields from array into an object of form name: Column + if(util.isArray(col.subfields)) { + col.subfields = lodash.chain(col.subfields) + .map(lodash.bind(function (subfield) { + return [subfield, new Column({ + table: this, + subfieldContainer: col, + name: subfield + })]; + }, this)) + .fromPairs() + .value(); + } } return col; @@ -65,23 +111,25 @@ Table.prototype.addColumn = function(col, options) { } else { return this; } - } else if(!!this[col.name] && (process.env.NODE_ENV !== 'test')) { + } else if(!!this[col.name] && (process.env.NODE_ENV === 'debug')) { console.log('Please notice that you have just defined the column "' + col.name + '". In order to access it, you need to use "table.getColumn(\'' + col.name + '\');"!'); } this.columns.push(col); - var property = col.property || col.name; + + function snakeToCamel(snakeName) { + return snakeName.replace(/[\-_]([a-z])/g, function(m, $1){ return $1.toUpperCase(); }); + } + + var property = col.property = col.property || (this.snakeToCamel ? snakeToCamel(col.name) : col.name); this[property] = this[property] || col; return this; }; Table.prototype.hasColumn = function(col) { - col = this.createColumn(col); - - var cols = this.columns.filter(function(column) { - return column.name === col.name; + var columnName = col instanceof Column ? col.name : col; + return this.columns.some(function(column) { + return column.property === columnName || column.name === columnName; }); - - return cols.length > 0; }; Table.prototype.getColumn = @@ -89,25 +137,43 @@ Table.prototype.get = function(colName) { for(var i = 0; i < this.columns.length; i++) { var col = this.columns[i]; - if(col.name == colName) { + if (colName === col.property || colName === col.name) { return col; } } - throw new Error('Table ' + this._name + ' does not have a column named ' + colName); + if(this.columnWhiteList) + return null; + throw new Error('Table ' + this._name + ' does not have a column or property named ' + colName); }; Table.prototype.getSchema = function() { return this._schema; }; +Table.prototype.setSchema = function(schema) { + this._schema = schema; +}; + Table.prototype.getName = function() { + if (this.sql && this.sql.dialectName=="mssql" && this.isTemporary) return "#"+this._name; return this._name; }; -Table.prototype.star = function() { +Table.prototype.star = function(options) { + options = options || {}; + if (options.prefix) { + return this.columns.map(function(column) { + return this[column.name].as(options.prefix + column.name); + }.bind(this)); + } + return new Column({table: this, star: true}); }; +Table.prototype.literal = function(literal) { + return new LiteralNode(literal); +}; + Table.prototype.count = function(alias) { var name = this.alias || this._name, col = new Column({table: this, star: true}); @@ -126,53 +192,36 @@ Table.prototype.select = function() { return query; }; -Table.prototype.from = function() { - var query = new Query(this); - query.from.apply(query, arguments); - return query; -}; - Table.prototype.subQuery = function(alias) { // create the query and pass it off var query = new Query(this); query.type = 'SUBQUERY'; query.alias = alias; + query.join = function(other) { + return new JoinNode('INNER', this.toNode(), other.toNode(), other); + }; return query; }; Table.prototype.insert = function() { var query = new Query(this); - query.insert.apply(query, arguments); - return query; -}; - -Table.prototype.update = function() { - var query = new Query(this); - query.update.apply(query, arguments); - return query; -}; - -Table.prototype['delete'] = function() { - var query = new Query(this); - query['delete'].apply(query, arguments); - return query; -}; - -Table.prototype.create = function() { - var query = new Query(this); - query.create.apply(query, arguments); - return query; -}; - -Table.prototype.drop = function() { - var query = new Query(this); - query.drop.apply(query, arguments); + if(!arguments[0] || (util.isArray(arguments[0]) && arguments[0].length === 0)){ + query.select.call(query, this.star()); + query.where.apply(query,["1=2"]); + } else { + query.insert.apply(query, arguments); + } return query; }; -Table.prototype.alter = function() { +Table.prototype.replace = function() { var query = new Query(this); - query.alter.apply(query, arguments); + if(!arguments[0] || (util.isArray(arguments[0]) && arguments[0].length === 0)){ + query.select.call(query, this.star()); + query.where.apply(query,["1=2"]); + } else { + query.replace.apply(query, arguments); + } return query; }; @@ -188,6 +237,10 @@ Table.prototype.leftJoin = function(other) { return new JoinNode('LEFT', this.toNode(), other.toNode()); }; +Table.prototype.leftJoinLateral = function(other) { + return new JoinNode('LEFT LATERAL', this.toNode(), other.toNode()); +}; + // auto-join tables based on column intropsection Table.prototype.joinTo = function(other) { return Joiner.leftJoin(this, other); @@ -205,26 +258,37 @@ Table.prototype.__defineGetter__("nodes", function() { return this.select(this.star()).nodes; }); -Table.prototype.where = function() { - var query = new Query(this); - query.where.apply(query, arguments); - return query; -}; - Table.prototype.and = function() { var query = new Query(this); query.where.apply(query, arguments); return query; }; -Table.prototype.or = function() { - var query = new Query(this); - query.or.apply(query, arguments); - return query; -}; - Table.prototype.indexes = function() { return new Query(this).indexes(); }; +var queryMethods = [ + 'alter', + 'create', + 'delete', + 'drop', + 'from', + 'limit', + 'offset', + 'or', + 'order', + 'truncate', + 'update', + 'where' +]; + +queryMethods.forEach(function (method) { + Table.prototype[method] = function () { + var query = new Query(this); + query[method].apply(query, arguments); + return query; + }; +}); + module.exports = Table; diff --git a/lib/types.d.ts b/lib/types.d.ts new file mode 100644 index 00000000..a7011f99 --- /dev/null +++ b/lib/types.d.ts @@ -0,0 +1,221 @@ + +/** + * This is an adaptation of https://github.com/doxout/anydb-sql/blob/4e4c0ff4a7f2efb7f820baaafea1f624f1ae0399/d.ts/anydb-sql.d.ts + * Whole project is MIT licensed, so, we can use it. We also feed back any + * improvements, questions, concerns. + */ +declare module "sql" { + + type SQLDialects = + | "mssql" + | "mysql" + | "oracle" + | "postgres" + | "sqlite" + ; + + interface OrderByValueNode {} + + interface Named { + name?: Name; + } + interface ColumnDefinition extends Named { + jsType?: Type; + dataType: string; + primaryKey?: boolean; + references?: { + table:string; + column: string; + onDelete?: 'restrict' | 'cascade' | 'no action' | 'set null' | 'set default'; + onUpdate?: 'restrict' | 'cascade' | 'no action' | 'set null' | 'set default'; + }; + notNull?: boolean; + unique?: boolean; + defaultValue?: Type; + } + + interface TableDefinition { + name: Name; + schema: string; + columns: {[CName in ((keyof Row) & string)]: ColumnDefinition}; + dialect?: SQLDialects; + isTemporary?: boolean; + foreignKeys?: { + table: string, + columns: (keyof Row)[], + refColumns: string[], + onDelete?: 'restrict' | 'cascade' | 'no action' | 'set null' | 'set default'; + onUpdate?: 'restrict' | 'cascade' | 'no action' | 'set null' | 'set default'; + } + } + + interface QueryLike { + values: any[] + text:string + } + + interface Executable { + toQuery():QueryLike; + } + + interface Queryable extends Executable { + where(...nodes:any[]):Query + delete():ModifyingQuery + select(star: Column): Query; + select(n1: Column):Query<{[N in N1]: T1}>; + select( + n1: Column, + n2: Column):Query<{[N in N1]: T1} & {[N in N2]: T2}> + select( + n1: Column, + n2: Column, + n3: Column):Query<{[N in N1]: T1} & {[N in N2]: T2} & {[N in N3]: T3}> + select(...nodesOrTables:any[]):Query + + } + + interface Query extends Executable, Queryable { + resultType: T; + + from(table:TableNode):Query + from(statement:string):Query + update(o:{[key: string]:any}):ModifyingQuery + update(o:{}):ModifyingQuery + group(...nodes:any[]):Query + order(...criteria:OrderByValueNode[]):Query + limit(l:number):Query + offset(o:number):Query + } + + interface SubQuery { + select(node:Column):SubQuery + select(...nodes: any[]):SubQuery + where(...nodes:any[]):SubQuery + from(table:TableNode):SubQuery + from(statement:string):SubQuery + group(...nodes:any[]):SubQuery + order(criteria:OrderByValueNode):SubQuery + exists():BinaryNode + notExists(): BinaryNode; + notExists(subQuery:SubQuery):BinaryNode + } + + + interface ModifyingQuery extends Executable { + returning(...nodes:any[]):Query + where(...nodes:any[]):ModifyingQuery + } + + interface TableNode { + join(table:TableNode):JoinTableNode + leftJoin(table:TableNode):JoinTableNode + } + + interface JoinTableNode extends TableNode { + on(filter:BinaryNode):TableNode + on(filter:string):TableNode + } + + interface CreateQuery extends Executable { + ifNotExists():Executable + } + interface DropQuery extends Executable { + ifExists():Executable + } + + type Columns = { + [Name in keyof T]: Column + } + type Table = TableNode & Queryable & Named & Columns & { + getName(): string; + getSchema(): string; + + literal(statement: string): any; + + create():CreateQuery + drop():DropQuery + as(name:OtherName):Table + update(o: Partial):ModifyingQuery + insert(row:T):ModifyingQuery + insert(rows:T[]):ModifyingQuery + select():Query + select(...nodes:any[]):Query + from(table:TableNode):Query + from(statement:string):Query + star():Column + subQuery():SubQuery + columns:Column[] + sql: SQL; + alter():AlterQuery; + indexes(): IndexQuery; + } + + interface AlterQuery extends Executable { + addColumn(column:Column): AlterQuery; + addColumn(name: string, options:string): AlterQuery; + dropColumn(column: Column|string): AlterQuery; + renameColumn(column: Column, newColumn: Column):AlterQuery; + renameColumn(column: Column, newName: string):AlterQuery; + renameColumn(name: string, newName: string):AlterQuery; + rename(newName: string): AlterQuery + } + interface IndexQuery { + create(): IndexCreationQuery; + create(indexName: string): IndexCreationQuery; + drop(indexName: string): Executable; + drop(...columns: Column[]): Executable + } + interface IndexCreationQuery extends Executable { + unique(): IndexCreationQuery; + using(name: string): IndexCreationQuery; + on(...columns: (Column|OrderByValueNode)[]): IndexCreationQuery; + withParser(parserName: string): IndexCreationQuery; + fulltext(): IndexCreationQuery; + spatial(): IndexCreationQuery; + } + + interface SQL { + functions: { + LOWER(c:Column):Column + } + } + + interface BinaryNode { + and(node:BinaryNode):BinaryNode + or(node:BinaryNode):BinaryNode + } + + interface Column { + name: Name + in(arr:T[]):BinaryNode + in(subQuery:SubQuery):BinaryNode + notIn(arr:T[]):BinaryNode + equals(node: T|Column):BinaryNode + notEquals(node: T|Column):BinaryNode + gte(node: T|Column):BinaryNode + lte(node: T|Column):BinaryNode + gt(node:T|Column):BinaryNode + lt(node: T|Column):BinaryNode + like(str:string):BinaryNode + multiply:{ + (node:Column):Column + (n:number):Column //todo check column names + } + isNull():BinaryNode + isNotNull():BinaryNode + //todo check column names + sum():Column + count():Column + count(name:string):Column + distinct():Column + as(name:OtherName):Column + ascending:OrderByValueNode + descending:OrderByValueNode + asc:OrderByValueNode + desc:OrderByValueNode + } + + function define(map:TableDefinition): Table; + function setDialect(dialect: SQLDialects): void; + +} diff --git a/package.json b/package.json index 1dc64752..dc250c01 100644 --- a/package.json +++ b/package.json @@ -2,22 +2,26 @@ "author": "brianc ", "name": "sql", "description": "sql builder", - "version": "0.25.0", + "version": "0.78.0", "homepage": "https://github.com/brianc/node-sql", + "license": "MIT", "repository": { "type": "git", "url": "git://github.com/brianc/node-sql.git" }, "main": "lib/", + "types": "lib/types.d.ts", "scripts": { - "test": "NODE_ENV=test ./node_modules/.bin/mocha" + "test": "node_modules/.bin/mocha", + "lint": "jshint lib test", + "posttest": "jshint lib test" }, "engines": { "node": "*" }, "dependencies": { "sliced": "0.0.x", - "lodash": "1.3.x" + "lodash": "4.1.x" }, "devDependencies": { "jshint": "*", diff --git a/runtests.js b/runtests.js new file mode 100644 index 00000000..ac89769b --- /dev/null +++ b/runtests.js @@ -0,0 +1,18 @@ +var childProcess = require("child_process"); +var path = require("path"); + +var env = process.env; +env.NODE_ENV = "test"; + +var options = { + env: env +}; + +var command = path.join(".", "node_modules", ".bin", "mocha"); +if (process.platform == "win32") command += ".cmd"; +var run = childProcess.spawn(command, [], options); +run.stdout.pipe(process.stdout); +run.stderr.pipe(process.stderr); +run.on('close', function(code) { + process.exit(code); +}); diff --git a/test/binary-clause-tests.js b/test/binary-clause-tests.js index 30607bcf..10c50737 100644 --- a/test/binary-clause-tests.js +++ b/test/binary-clause-tests.js @@ -1,4 +1,3 @@ -/* global test */ 'use strict'; var assert = require('assert'); @@ -27,6 +26,19 @@ test('operators', function() { assert.equal(Foo.baz.multiply(1).operator, '*'); assert.equal(Foo.baz.leftShift(1).operator, '<<'); assert.equal(Foo.baz.rightShift(1).operator, '>>'); + assert.equal(Foo.baz.bitwiseAnd(1).operator, '&'); + assert.equal(Foo.baz.bitwiseNot(1).operator, '~'); + assert.equal(Foo.baz.bitwiseOr(1).operator, '|'); + assert.equal(Foo.baz.bitwiseXor(1).operator, '#'); assert.equal(Foo.baz.divide(1).operator, '/'); assert.equal(Foo.baz.modulo(1).operator, '%'); + assert.equal(Foo.baz.regex(1).operator, '~'); + assert.equal(Foo.baz.iregex(1).operator, '~*'); + assert.equal(Foo.baz.notRegex(1).operator, '!~'); + assert.equal(Foo.baz.notIregex(1).operator, '!~*'); + assert.equal(Foo.baz.regexp(1).operator, 'REGEXP'); + assert.equal(Foo.baz.rlike(1).operator, 'RLIKE'); + assert.equal(Foo.baz.ilike('asdf').operator, 'ILIKE'); + assert.equal(Foo.baz.notIlike('asdf').operator, 'NOT ILIKE'); + assert.equal(Foo.baz.match('asdf').operator, '@@'); }); diff --git a/test/clause-definition.js b/test/clause-definition.js index 6f918dad..ebbf936d 100644 --- a/test/clause-definition.js +++ b/test/clause-definition.js @@ -1,4 +1,3 @@ -/* global test */ 'use strict'; var assert = require('assert'); diff --git a/test/column-tests.js b/test/column-tests.js index 0db0bca3..d0b95a14 100644 --- a/test/column-tests.js +++ b/test/column-tests.js @@ -1,10 +1,12 @@ +'use strict'; + var assert = require('assert'); var sql = require(__dirname + '/../lib'); describe('column', function() { var table = sql.define({ name: 'user', - columns: ['id', 'created'] + columns: ['id', 'created', 'alias'] }); it('can be accessed by property and array', function() { @@ -16,6 +18,10 @@ describe('column', function() { assert.equal(table.id.toQuery().text, '"user"."id"'); }); + it('works with a column name of "alias"', function() { + assert.equal(table.alias.toQuery().text, '"user"."alias"'); + }); + it('respects AS rename', function() { assert.equal(table.id.as('userId').toQuery().text, '"user"."id" AS "userId"'); }); @@ -29,5 +35,97 @@ describe('column', function() { var col = subquery.subId.toQuery().text; assert.equal(col, '"subTable"."subId"'); }); + + describe('property', function() { + var table = sql.define({ + name: 'roundtrip', + columns: { + column_name: { property: 'propertyName' } + } + }); + it('used as alias when !== column name', function() { + assert.equal(table.propertyName.toQuery().text, '"roundtrip"."column_name" AS "propertyName"'); + }); + it('uses explicit alias when !== column name', function() { + assert.equal(table.propertyName.as('alias').toQuery().text, '"roundtrip"."column_name" AS "alias"'); + }); + it('maps to column name in insert', function() { + assert.equal(table.insert({propertyName:'propVal'}).toQuery().text, 'INSERT INTO "roundtrip" ("column_name") VALUES ($1)'); + }); + it('maps to column name in update', function() { + assert.equal(table.update({propertyName:'propVal'}).toQuery().text, 'UPDATE "roundtrip" SET "column_name" = $1'); + }); + it('explicitly selected by *', function() { + assert.equal(table.select(table.star()).from(table).toQuery().text, 'SELECT "roundtrip"."column_name" AS "propertyName" FROM "roundtrip"'); + }); + }); + + describe('autoGenerate', function() { + var table = sql.define({ + name: 'ag', + columns: { + id: {autoGenerated: true}, + name: {} + } + }); + it('does not include auto generated columns in insert', function() { + assert.equal(table.insert({id:0, name:'name'}).toQuery().text,'INSERT INTO "ag" ("name") VALUES ($1)'); + }); + it('does not include auto generated columns in update', function() { + assert.equal(table.update({id:0, name:'name'}).toQuery().text,'UPDATE "ag" SET "name" = $1'); + }); + }); + + describe('white listed', function() { + var table = sql.define({ + name: 'wl', + columnWhiteList: true, + columns: ['id', 'name'] + }); + it('excludes insert properties that are not a column', function() { + assert.equal(table.insert({id:0, _private:'_private', name:'name'}).toQuery().text, 'INSERT INTO "wl" ("id", "name") VALUES ($1, $2)'); + }); + it('excludes update properties that are not a column', function() { + assert.equal(table.update({id:0, _private:'_private', name:'name'}).toQuery().text, 'UPDATE "wl" SET "id" = $1, "name" = $2'); + }); + }); + + describe('not white listed', function() { + var table = sql.define({ + name: 'wl', + columns: ['id', 'name'] + }); + it('throws for insert properties that are not a column', function() { + assert.throws(function() { table.insert({id:0, _private:'_private', name:'name'}); }, Error); + }); + it('throws for update properties that are not a column', function() { + assert.throws(function() { table.update({id:0, _private:'_private', name:'name'}); }, Error); + }); + }); + + describe('snake to camel', function() { + var table = sql.define({ + name: 'sc', + snakeToCamel: true, + columns: { + make_me_camel: {}, + not_to_camel: {property: 'not2Cam'} + } + }); + it('for snake column names with no explicit property name', function(){ + assert.equal(table.makeMeCamel.toQuery().text, '"sc"."make_me_camel" AS "makeMeCamel"'); + }); + it('but not when with explicit property name', function() { + assert.equal(table.not2Cam.toQuery().text, '"sc"."not_to_camel" AS "not2Cam"'); + }); + it('does not use property alias within CASE ... END', function() { + assert.equal(table.makeMeCamel.case([table.makeMeCamel.equals(0)],[table.makeMeCamel]).as('rename').toQuery().text, + '(CASE WHEN ("sc"."make_me_camel" = $1) THEN "sc"."make_me_camel" END) AS "rename"'); + }); + it('respects AS rename in RETURNING clause', function() { + assert.equal(table.update({makeMeCamel:0}).returning(table.makeMeCamel.as('rename')).toQuery().text, + 'UPDATE "sc" SET "make_me_camel" = $1 RETURNING "make_me_camel" AS "rename"'); + }); + }); }); }); diff --git a/test/dialects/aggregate-tests.js b/test/dialects/aggregate-tests.js index 84c38873..59d48d57 100644 --- a/test/dialects/aggregate-tests.js +++ b/test/dialects/aggregate-tests.js @@ -1,9 +1,9 @@ -/* global test */ 'use strict'; -var assert = require('assert'); var Harness = require('./support'); var post = Harness.definePostTable(); +var customerAlias = Harness.defineCustomerAliasTable(); + Harness.test({ query: post.select(post.count()), @@ -16,8 +16,16 @@ Harness.test({ string: 'SELECT COUNT("post".*) AS "post_count" FROM "post"' }, mysql: { - text : 'SELECT COUNT(`post`.*) AS `post_count` FROM `post`', - string: 'SELECT COUNT(`post`.*) AS `post_count` FROM `post`' + text : 'SELECT COUNT(*) AS `post_count` FROM `post`', + string: 'SELECT COUNT(*) AS `post_count` FROM `post`' + }, + mssql: { + text : 'SELECT COUNT(*) AS [post_count] FROM [post]', + string: 'SELECT COUNT(*) AS [post_count] FROM [post]' + }, + oracle: { + text : 'SELECT COUNT(*) "post_count" FROM "post"', + string: 'SELECT COUNT(*) "post_count" FROM "post"' }, params: [] }); @@ -33,8 +41,16 @@ Harness.test({ string: 'SELECT COUNT("post".*) AS "post_count" FROM "post"' }, msyql: { - text : 'SELECT COUNT(`post`.*) AS `post_count` FROM `post`', - string: 'SELECT COUNT(`post`.*) AS `post_count` FROM `post`' + text : 'SELECT COUNT(*) AS `post_count` FROM `post`', + string: 'SELECT COUNT(*) AS `post_count` FROM `post`' + }, + mssql: { + text : 'SELECT COUNT(*) AS [post_count] FROM [post]', + string: 'SELECT COUNT(*) AS [post_count] FROM [post]' + }, + oracle: { + text : 'SELECT COUNT(*) "post_count" FROM "post"', + string: 'SELECT COUNT(*) "post_count" FROM "post"' }, params: [] }); @@ -50,8 +66,16 @@ Harness.test({ string: 'SELECT COUNT("post".*) AS "post_amount" FROM "post"' }, mysql: { - text : 'SELECT COUNT(`post`.*) AS `post_amount` FROM `post`', - string: 'SELECT COUNT(`post`.*) AS `post_amount` FROM `post`' + text : 'SELECT COUNT(*) AS `post_amount` FROM `post`', + string: 'SELECT COUNT(*) AS `post_amount` FROM `post`' + }, + mssql: { + text : 'SELECT COUNT(*) AS [post_amount] FROM [post]', + string: 'SELECT COUNT(*) AS [post_amount] FROM [post]' + }, + oracle: { + text : 'SELECT COUNT(*) "post_amount" FROM "post"', + string: 'SELECT COUNT(*) "post_amount" FROM "post"' }, params: [] }); @@ -70,6 +94,14 @@ Harness.test({ text : 'SELECT COUNT(`post`.`content`) AS `content_count` FROM `post`', string: 'SELECT COUNT(`post`.`content`) AS `content_count` FROM `post`' }, + mssql: { + text : 'SELECT COUNT([post].[content]) AS [content_count] FROM [post]', + string: 'SELECT COUNT([post].[content]) AS [content_count] FROM [post]' + }, + oracle: { + text : 'SELECT COUNT("post"."content") "content_count" FROM "post"', + string: 'SELECT COUNT("post"."content") "content_count" FROM "post"' + }, params: [] }); @@ -87,6 +119,14 @@ Harness.test({ text : 'SELECT COUNT(`post`.`content`) AS `content_count` FROM `post`', string: 'SELECT COUNT(`post`.`content`) AS `content_count` FROM `post`' }, + mssql: { + text : 'SELECT COUNT([post].[content]) AS [content_count] FROM [post]', + string: 'SELECT COUNT([post].[content]) AS [content_count] FROM [post]' + }, + oracle: { + text : 'SELECT COUNT("post"."content") "content_count" FROM "post"', + string: 'SELECT COUNT("post"."content") "content_count" FROM "post"' + }, params: [] }); @@ -104,6 +144,35 @@ Harness.test({ text : 'SELECT COUNT(`post`.`content`) AS `content_count` FROM `post`', string: 'SELECT COUNT(`post`.`content`) AS `content_count` FROM `post`' }, + mssql: { + text : 'SELECT COUNT([post].[content]) AS [content_count] FROM [post]', + string: 'SELECT COUNT([post].[content]) AS [content_count] FROM [post]' + }, + oracle: { + text : 'SELECT COUNT("post"."content") "content_count" FROM "post"', + string: 'SELECT COUNT("post"."content") "content_count" FROM "post"' + }, + params: [] +}); + +Harness.test({ + query: customerAlias.select(customerAlias.count()), + pg: { + text : 'SELECT COUNT("customer".*) AS "customer_count" FROM "customer"', + string: 'SELECT COUNT("customer".*) AS "customer_count" FROM "customer"' + }, + sqlite: { + text : 'SELECT COUNT("customer".*) AS "customer_count" FROM "customer"', + string: 'SELECT COUNT("customer".*) AS "customer_count" FROM "customer"' + }, + mysql: { + text : 'SELECT COUNT(*) AS `customer_count` FROM `customer`', + string: 'SELECT COUNT(*) AS `customer_count` FROM `customer`' + }, + oracle: { + text : 'SELECT COUNT(*) "customer_count" FROM "customer"', + string: 'SELECT COUNT(*) "customer_count" FROM "customer"' + }, params: [] }); @@ -121,6 +190,14 @@ Harness.test({ text : 'SELECT MIN(`post`.`id`) AS `id_min` FROM `post`', string: 'SELECT MIN(`post`.`id`) AS `id_min` FROM `post`' }, + mssql: { + text : 'SELECT MIN([post].[id]) AS [id_min] FROM [post]', + string: 'SELECT MIN([post].[id]) AS [id_min] FROM [post]' + }, + oracle: { + text : 'SELECT MIN("post"."id") "id_min" FROM "post"', + string: 'SELECT MIN("post"."id") "id_min" FROM "post"' + }, params: [] }); @@ -138,6 +215,14 @@ Harness.test({ text : 'SELECT MIN(`post`.`id`) AS `min_id` FROM `post`', string: 'SELECT MIN(`post`.`id`) AS `min_id` FROM `post`' }, + mssql: { + text : 'SELECT MIN([post].[id]) AS [min_id] FROM [post]', + string: 'SELECT MIN([post].[id]) AS [min_id] FROM [post]' + }, + oracle: { + text : 'SELECT MIN("post"."id") "min_id" FROM "post"', + string: 'SELECT MIN("post"."id") "min_id" FROM "post"' + }, params: [] }); @@ -155,6 +240,14 @@ Harness.test({ text : 'SELECT MIN(`post`.`id`) AS `min_id` FROM `post`', string: 'SELECT MIN(`post`.`id`) AS `min_id` FROM `post`' }, + mssql: { + text : 'SELECT MIN([post].[id]) AS [min_id] FROM [post]', + string: 'SELECT MIN([post].[id]) AS [min_id] FROM [post]' + }, + oracle: { + text : 'SELECT MIN("post"."id") "min_id" FROM "post"', + string: 'SELECT MIN("post"."id") "min_id" FROM "post"' + }, params: [] }); @@ -172,6 +265,14 @@ Harness.test({ text : 'SELECT MAX(`post`.`id`) AS `id_max` FROM `post`', string: 'SELECT MAX(`post`.`id`) AS `id_max` FROM `post`' }, + mssql: { + text : 'SELECT MAX([post].[id]) AS [id_max] FROM [post]', + string: 'SELECT MAX([post].[id]) AS [id_max] FROM [post]' + }, + oracle: { + text : 'SELECT MAX("post"."id") "id_max" FROM "post"', + string: 'SELECT MAX("post"."id") "id_max" FROM "post"' + }, params: [] }); @@ -189,6 +290,14 @@ Harness.test({ text : 'SELECT MAX(`post`.`id`) AS `max_id` FROM `post`', string: 'SELECT MAX(`post`.`id`) AS `max_id` FROM `post`' }, + mssql: { + text : 'SELECT MAX([post].[id]) AS [max_id] FROM [post]', + string: 'SELECT MAX([post].[id]) AS [max_id] FROM [post]' + }, + oracle: { + text : 'SELECT MAX("post"."id") "max_id" FROM "post"', + string: 'SELECT MAX("post"."id") "max_id" FROM "post"' + }, params: [] }); @@ -206,6 +315,14 @@ Harness.test({ text : 'SELECT MAX(`post`.`id`) AS `max_id` FROM `post`', string: 'SELECT MAX(`post`.`id`) AS `max_id` FROM `post`' }, + mssql: { + text : 'SELECT MAX([post].[id]) AS [max_id] FROM [post]', + string: 'SELECT MAX([post].[id]) AS [max_id] FROM [post]' + }, + oracle: { + text : 'SELECT MAX("post"."id") "max_id" FROM "post"', + string: 'SELECT MAX("post"."id") "max_id" FROM "post"' + }, params: [] }); @@ -223,6 +340,15 @@ Harness.test({ text : 'SELECT SUM(`post`.`id`) AS `id_sum` FROM `post`', string: 'SELECT SUM(`post`.`id`) AS `id_sum` FROM `post`' }, + mssql: { + text : 'SELECT SUM([post].[id]) AS [id_sum] FROM [post]', + string: 'SELECT SUM([post].[id]) AS [id_sum] FROM [post]' + }, + oracle: { + text : 'SELECT SUM("post"."id") "id_sum" FROM "post"', + string: 'SELECT SUM("post"."id") "id_sum" FROM "post"' + }, + params: [] }); Harness.test({ @@ -239,6 +365,15 @@ Harness.test({ text : 'SELECT SUM(`post`.`id`) AS `sum_id` FROM `post`', string: 'SELECT SUM(`post`.`id`) AS `sum_id` FROM `post`' }, + mssql: { + text : 'SELECT SUM([post].[id]) AS [sum_id] FROM [post]', + string: 'SELECT SUM([post].[id]) AS [sum_id] FROM [post]' + }, + oracle: { + text : 'SELECT SUM("post"."id") "sum_id" FROM "post"', + string: 'SELECT SUM("post"."id") "sum_id" FROM "post"' + }, + params: [] }); Harness.test({ @@ -255,6 +390,15 @@ Harness.test({ text : 'SELECT SUM(`post`.`id`) AS `sum_id` FROM `post`', string: 'SELECT SUM(`post`.`id`) AS `sum_id` FROM `post`' }, + mssql: { + text : 'SELECT SUM([post].[id]) AS [sum_id] FROM [post]', + string: 'SELECT SUM([post].[id]) AS [sum_id] FROM [post]' + }, + oracle: { + text : 'SELECT SUM("post"."id") "sum_id" FROM "post"', + string: 'SELECT SUM("post"."id") "sum_id" FROM "post"' + }, + params: [] }); Harness.test({ @@ -271,6 +415,15 @@ Harness.test({ text : 'SELECT AVG(`post`.`id`) AS `id_avg` FROM `post`', string: 'SELECT AVG(`post`.`id`) AS `id_avg` FROM `post`' }, + mssql: { + text : 'SELECT AVG([post].[id]) AS [id_avg] FROM [post]', + string: 'SELECT AVG([post].[id]) AS [id_avg] FROM [post]' + }, + oracle: { + text : 'SELECT AVG("post"."id") "id_avg" FROM "post"', + string: 'SELECT AVG("post"."id") "id_avg" FROM "post"' + }, + params: [] }); Harness.test({ @@ -287,6 +440,15 @@ Harness.test({ text : 'SELECT AVG(`post`.`id`) AS `avg_id` FROM `post`', string: 'SELECT AVG(`post`.`id`) AS `avg_id` FROM `post`' }, + mssql: { + text : 'SELECT AVG([post].[id]) AS [avg_id] FROM [post]', + string: 'SELECT AVG([post].[id]) AS [avg_id] FROM [post]' + }, + oracle: { + text : 'SELECT AVG("post"."id") "avg_id" FROM "post"', + string: 'SELECT AVG("post"."id") "avg_id" FROM "post"' + }, + params: [] }); Harness.test({ @@ -303,4 +465,13 @@ Harness.test({ text : 'SELECT AVG(`post`.`id`) AS `avg_id` FROM `post`', string: 'SELECT AVG(`post`.`id`) AS `avg_id` FROM `post`' }, + mssql: { + text : 'SELECT AVG([post].[id]) AS [avg_id] FROM [post]', + string: 'SELECT AVG([post].[id]) AS [avg_id] FROM [post]' + }, + oracle: { + text : 'SELECT AVG("post"."id") "avg_id" FROM "post"', + string: 'SELECT AVG("post"."id") "avg_id" FROM "post"' + }, + params: [] }); diff --git a/test/dialects/alias-tests.js b/test/dialects/alias-tests.js index e0fc8709..32db9308 100644 --- a/test/dialects/alias-tests.js +++ b/test/dialects/alias-tests.js @@ -2,8 +2,7 @@ var Harness = require('./support'); var customer = Harness.defineCustomerTable(); -var post = Harness.definePostTable(); -var Table = require(__dirname + '/../../lib/table'); +var Sql = require('../../lib').setDialect('postgres'); Harness.test({ query: customer.select(customer.name.isNull().as('nameIsNull')), @@ -19,6 +18,14 @@ Harness.test({ text : 'SELECT (`customer`.`name` IS NULL) AS `nameIsNull` FROM `customer`', string: 'SELECT (`customer`.`name` IS NULL) AS `nameIsNull` FROM `customer`' }, + mssql: { + text : 'SELECT ([customer].[name] IS NULL) AS [nameIsNull] FROM [customer]', + string: 'SELECT ([customer].[name] IS NULL) AS [nameIsNull] FROM [customer]' + }, + oracle: { + text : 'SELECT ("customer"."name" IS NULL) "nameIsNull" FROM "customer"', + string: 'SELECT ("customer"."name" IS NULL) "nameIsNull" FROM "customer"' + }, params: [] }); @@ -36,6 +43,14 @@ Harness.test({ text : 'SELECT (`customer`.`name` + `customer`.`age`) AS `nameAndAge` FROM `customer` WHERE ((`customer`.`age` > ?) AND (`customer`.`age` < ?))', string: 'SELECT (`customer`.`name` + `customer`.`age`) AS `nameAndAge` FROM `customer` WHERE ((`customer`.`age` > 10) AND (`customer`.`age` < 20))' }, + mssql: { + text : 'SELECT ([customer].[name] + [customer].[age]) AS [nameAndAge] FROM [customer] WHERE (([customer].[age] > @1) AND ([customer].[age] < @2))', + string: 'SELECT ([customer].[name] + [customer].[age]) AS [nameAndAge] FROM [customer] WHERE (([customer].[age] > 10) AND ([customer].[age] < 20))' + }, + oracle: { + text : 'SELECT ("customer"."name" + "customer"."age") "nameAndAge" FROM "customer" WHERE (("customer"."age" > :1) AND ("customer"."age" < :2))', + string: 'SELECT ("customer"."name" + "customer"."age") "nameAndAge" FROM "customer" WHERE (("customer"."age" > 10) AND ("customer"."age" < 20))' + }, params: [10, 20] }); @@ -53,5 +68,55 @@ Harness.test({ text : 'SELECT (`customer`.`age` BETWEEN ? AND ?) AS `ageBetween` FROM `customer`', string: 'SELECT (`customer`.`age` BETWEEN 10 AND 20) AS `ageBetween` FROM `customer`' }, + mssql: { + text : 'SELECT ([customer].[age] BETWEEN @1 AND @2) AS [ageBetween] FROM [customer]', + string: 'SELECT ([customer].[age] BETWEEN 10 AND 20) AS [ageBetween] FROM [customer]' + }, + oracle: { + text : 'SELECT ("customer"."age" BETWEEN :1 AND :2) "ageBetween" FROM "customer"', + string: 'SELECT ("customer"."age" BETWEEN 10 AND 20) "ageBetween" FROM "customer"' + }, + params: [10, 20] +}); + +Harness.test({ + query: customer.select(Sql.functions.ROUND(customer.age.as('ageBetween'), 2)), + pg: { + text : 'SELECT ROUND("customer"."age", $1) FROM "customer"', + string: 'SELECT ROUND("customer"."age", 2) FROM "customer"' + }, + sqlite: { + text : 'SELECT ROUND("customer"."age", $1) FROM "customer"', + string: 'SELECT ROUND("customer"."age", 2) FROM "customer"' + }, + mysql: { + text : 'SELECT ROUND(`customer`.`age`, ?) FROM `customer`', + string: 'SELECT ROUND(`customer`.`age`, 2) FROM `customer`' + }, + mssql: { + text : 'SELECT ROUND([customer].[age], @1) FROM [customer]', + string: 'SELECT ROUND([customer].[age], 2) FROM [customer]' + }, + oracle: { + text : 'SELECT ROUND("customer"."age", :1) FROM "customer"', + string: 'SELECT ROUND("customer"."age", 2) FROM "customer"' + }, + params: [2] +}); + +Harness.test({ + query: customer.select(customer.age.notBetween(10, 20).as('ageNotBetween')), + pg: { + text : 'SELECT ("customer"."age" NOT BETWEEN $1 AND $2) AS "ageNotBetween" FROM "customer"', + string: 'SELECT ("customer"."age" NOT BETWEEN 10 AND 20) AS "ageNotBetween" FROM "customer"' + }, + sqlite: { + text : 'SELECT ("customer"."age" NOT BETWEEN $1 AND $2) AS "ageNotBetween" FROM "customer"', + string: 'SELECT ("customer"."age" NOT BETWEEN 10 AND 20) AS "ageNotBetween" FROM "customer"' + }, + mysql: { + text : 'SELECT (`customer`.`age` NOT BETWEEN ? AND ?) AS `ageNotBetween` FROM `customer`', + string: 'SELECT (`customer`.`age` NOT BETWEEN 10 AND 20) AS `ageNotBetween` FROM `customer`' + }, params: [10, 20] }); diff --git a/test/dialects/alter-table-tests.js b/test/dialects/alter-table-tests.js index 71a5c396..d050da3d 100644 --- a/test/dialects/alter-table-tests.js +++ b/test/dialects/alter-table-tests.js @@ -18,6 +18,14 @@ Harness.test({ text : 'ALTER TABLE `post` DROP COLUMN `content`', string: 'ALTER TABLE `post` DROP COLUMN `content`' }, + mssql: { + text : 'ALTER TABLE [post] DROP COLUMN [content]', + string: 'ALTER TABLE [post] DROP COLUMN [content]' + }, + oracle: { + text : 'ALTER TABLE "post" DROP ("content")', + string: 'ALTER TABLE "post" DROP ("content")' + }, params: [] }); @@ -35,6 +43,14 @@ Harness.test({ text : 'ALTER TABLE `post` DROP COLUMN `content`, DROP COLUMN `userId`', string: 'ALTER TABLE `post` DROP COLUMN `content`, DROP COLUMN `userId`' }, + mssql: { + text : 'ALTER TABLE [post] DROP COLUMN [content], [userId]', + string: 'ALTER TABLE [post] DROP COLUMN [content], [userId]' + }, + oracle: { + text : 'ALTER TABLE "post" DROP ("content", "userId")', + string: 'ALTER TABLE "post" DROP ("content", "userId")' + }, params: [] }); @@ -52,6 +68,14 @@ Harness.test({ text : 'ALTER TABLE `post` DROP COLUMN `content`, DROP COLUMN `userId`', string: 'ALTER TABLE `post` DROP COLUMN `content`, DROP COLUMN `userId`' }, + mssql: { + text : 'ALTER TABLE [post] DROP COLUMN [content], [userId]', + string: 'ALTER TABLE [post] DROP COLUMN [content], [userId]' + }, + oracle: { + text : 'ALTER TABLE "post" DROP ("content", "userId")', + string: 'ALTER TABLE "post" DROP ("content", "userId")' + }, params: [] }); @@ -69,6 +93,10 @@ Harness.test({ text : 'ALTER TABLE `post` RENAME TO `posts`', string: 'ALTER TABLE `post` RENAME TO `posts`' }, + mssql: { + text : 'EXEC sp_rename [post], [posts]', + string: 'EXEC sp_rename [post], [posts]' + }, params: [] }); @@ -98,6 +126,14 @@ Harness.test({ text : 'ALTER TABLE `group` ADD COLUMN `id` varchar(100)', string: 'ALTER TABLE `group` ADD COLUMN `id` varchar(100)' }, + mssql: { + text : 'ALTER TABLE [group] ADD [id] varchar(100)', + string: 'ALTER TABLE [group] ADD [id] varchar(100)' + }, + oracle: { + text : 'ALTER TABLE "group" ADD ("id" varchar(100))', + string: 'ALTER TABLE "group" ADD ("id" varchar(100))' + }, params: [] }); @@ -115,6 +151,14 @@ Harness.test({ text : 'ALTER TABLE `group` ADD COLUMN `id` varchar(100), ADD COLUMN `userId` varchar(100)', string: 'ALTER TABLE `group` ADD COLUMN `id` varchar(100), ADD COLUMN `userId` varchar(100)' }, + mssql: { + text : 'ALTER TABLE [group] ADD [id] varchar(100), [userId] varchar(100)', + string: 'ALTER TABLE [group] ADD [id] varchar(100), [userId] varchar(100)' + }, + oracle: { + text : 'ALTER TABLE "group" ADD ("id" varchar(100), "userId" varchar(100))', + string: 'ALTER TABLE "group" ADD ("id" varchar(100), "userId" varchar(100))' + }, params: [] }); @@ -132,6 +176,14 @@ Harness.test({ text : 'ALTER TABLE `group` ADD COLUMN `id` varchar(100), ADD COLUMN `userId` varchar(100)', string: 'ALTER TABLE `group` ADD COLUMN `id` varchar(100), ADD COLUMN `userId` varchar(100)' }, + mssql: { + text : 'ALTER TABLE [group] ADD [id] varchar(100), [userId] varchar(100)', + string: 'ALTER TABLE [group] ADD [id] varchar(100), [userId] varchar(100)' + }, + oracle: { + text : 'ALTER TABLE "group" ADD ("id" varchar(100), "userId" varchar(100))', + string: 'ALTER TABLE "group" ADD ("id" varchar(100), "userId" varchar(100))' + }, params: [] }); @@ -149,6 +201,10 @@ Harness.test({ text : 'Sqlite cannot rename columns', throws: true }, + mssql: { + text : 'EXEC sp_rename \'[group].[userId]\', [newUserId], \'COLUMN\'', + string: 'EXEC sp_rename \'[group].[userId]\', [newUserId], \'COLUMN\'' + }, params: [] }); @@ -166,6 +222,10 @@ Harness.test({ text : 'ALTER TABLE `group` CHANGE COLUMN `userId` `newUserId` varchar(100)', string: 'ALTER TABLE `group` CHANGE COLUMN `userId` `newUserId` varchar(100)' }, + mssql: { + text : 'EXEC sp_rename \'[group].[userId]\', [newUserId], \'COLUMN\'', + string: 'EXEC sp_rename \'[group].[userId]\', [newUserId], \'COLUMN\'' + }, params: [] }); @@ -183,6 +243,10 @@ Harness.test({ text : 'ALTER TABLE `group` CHANGE COLUMN `userId` `id` varchar(100)', string: 'ALTER TABLE `group` CHANGE COLUMN `userId` `id` varchar(100)' }, + mssql: { + text : 'EXEC sp_rename \'[group].[userId]\', [id], \'COLUMN\'', + string: 'EXEC sp_rename \'[group].[userId]\', [id], \'COLUMN\'' + }, params: [] }); @@ -208,5 +272,67 @@ Harness.test({ sqlite: { text : 'Sqlite cannot rename columns', throws: true + }, + mssql: { + text : 'EXEC sp_rename \'[UserWithSignature].[Signature]\', [sig], \'COLUMN\'', + string: 'EXEC sp_rename \'[UserWithSignature].[Signature]\', [sig], \'COLUMN\'' } }); + +var post = Table.define({ + name: 'post', + columns: [{ + name: 'userId', + dataType: 'int', + references: { + table: 'user', + column: 'id' + } + }, { + name: 'picture', + dataType: 'varchar(100)', + references: {} + }] +}); + +Harness.test({ + query: post.alter().addColumn(post.userId), + pg: { + text : 'ALTER TABLE "post" ADD COLUMN "userId" int REFERENCES "user"("id")', + string: 'ALTER TABLE "post" ADD COLUMN "userId" int REFERENCES "user"("id")' + }, + sqlite: { + text : 'ALTER TABLE "post" ADD COLUMN "userId" int REFERENCES "user"("id")', + string: 'ALTER TABLE "post" ADD COLUMN "userId" int REFERENCES "user"("id")' + }, + mysql: { + text : 'ALTER TABLE `post` ADD COLUMN `userId` int REFERENCES `user`(`id`)', + string: 'ALTER TABLE `post` ADD COLUMN `userId` int REFERENCES `user`(`id`)' + }, + oracle: { + text : 'ALTER TABLE "post" ADD ("userId" int REFERENCES "user"("id"))', + string: 'ALTER TABLE "post" ADD ("userId" int REFERENCES "user"("id"))' + }, + params: [] +}); + +Harness.test({ + query: post.alter().addColumn(post.picture), + pg: { + text : 'ALTER TABLE "post" ADD COLUMN "picture" varchar(100)', + string: 'ALTER TABLE "post" ADD COLUMN "picture" varchar(100)' + }, + sqlite: { + text : 'ALTER TABLE "post" ADD COLUMN "picture" varchar(100)', + string: 'ALTER TABLE "post" ADD COLUMN "picture" varchar(100)' + }, + mysql: { + text : 'ALTER TABLE `post` ADD COLUMN `picture` varchar(100)', + string: 'ALTER TABLE `post` ADD COLUMN `picture` varchar(100)' + }, + oracle: { + text : 'ALTER TABLE "post" ADD ("picture" varchar(100))', + string: 'ALTER TABLE "post" ADD ("picture" varchar(100))' + }, + params: [] +}); diff --git a/test/dialects/array-tests.js b/test/dialects/array-tests.js new file mode 100644 index 00000000..f4191400 --- /dev/null +++ b/test/dialects/array-tests.js @@ -0,0 +1,96 @@ +'use strict'; + +var Harness = require('./support'); +var post = Harness.definePostTable(); +var Sql = require('../../lib'); + +// Array columns +Harness.test({ + query: post.update({ + tags: post.tags.concat(Sql.array('nodejs')) + }), + pg: { + text : 'UPDATE "post" SET "tags" = ("post"."tags" || ARRAY[$1])', + string: 'UPDATE "post" SET "tags" = ("post"."tags" || ARRAY[\'nodejs\'])' + }, + params: ['nodejs'] +}); + +Harness.test({ + query: post.select( + post.tags.contains(Sql.array('nodejs', 'js')) + ), + pg: { + text : 'SELECT ("post"."tags" @> ARRAY[$1, $2]) FROM "post"', + string: 'SELECT ("post"."tags" @> ARRAY[\'nodejs\', \'js\']) FROM "post"' + }, + params: ['nodejs', 'js'] +}); + +Harness.test({ + query: post.select( + post.tags.containedBy(Sql.array('nodejs', 'js')) + ), + pg: { + text : 'SELECT ("post"."tags" <@ ARRAY[$1, $2]) FROM "post"', + string: 'SELECT ("post"."tags" <@ ARRAY[\'nodejs\', \'js\']) FROM "post"' + }, + params: ['nodejs', 'js'] +}); + +Harness.test({ + query: post.select( + post.tags.overlap(Sql.array('nodejs', 'js')) + ), + pg: { + text : 'SELECT ("post"."tags" && ARRAY[$1, $2]) FROM "post"', + string: 'SELECT ("post"."tags" && ARRAY[\'nodejs\', \'js\']) FROM "post"' + }, + params: ['nodejs', 'js'] +}); + +Harness.test({ + query: post.select(post.tags.slice(2,3)), + pg: { + text : 'SELECT ("post"."tags")[$1:$2] FROM "post"', + string: 'SELECT ("post"."tags")[2:3] FROM "post"' + }, + params: [2, 3] +}); + +Harness.test({ + query: post.select(post.tags.at(2)), + pg: { + text : 'SELECT ("post"."tags")[$1] FROM "post"', + string: 'SELECT ("post"."tags")[2] FROM "post"' + }, + params: [2] +}); + +// Array literals +Harness.test({ + query: post.select(Sql.array(1,2,3)), + pg: { + text : 'SELECT ARRAY[$1, $2, $3] FROM "post"', + string: 'SELECT ARRAY[1, 2, 3] FROM "post"' + }, + params: [1, 2, 3] +}); + +Harness.test({ + query: post.select(Sql.array(1,2,3).slice(2,3)), + pg: { + text : 'SELECT (ARRAY[$1, $2, $3])[$4:$5] FROM "post"', + string: 'SELECT (ARRAY[1, 2, 3])[2:3] FROM "post"' + }, + params: [1,2,3,2,3] +}); + +Harness.test({ + query: post.select(Sql.array(1,2,3).at(2)), + pg: { + text : 'SELECT (ARRAY[$1, $2, $3])[$4] FROM "post"', + string: 'SELECT (ARRAY[1, 2, 3])[2] FROM "post"' + }, + params: [1,2,3,2] +}); diff --git a/test/dialects/binary-clause-tests.js b/test/dialects/binary-clause-tests.js index a2002225..69c355fe 100644 --- a/test/dialects/binary-clause-tests.js +++ b/test/dialects/binary-clause-tests.js @@ -2,8 +2,9 @@ var Harness = require('./support'); var customer = Harness.defineCustomerTable(); +var customerAlias = Harness.defineCustomerAliasTable(); var post = Harness.definePostTable(); -var Table = require(__dirname + '/../../lib/table'); + Harness.test({ query: customer.select(customer.name.plus(customer.age)), @@ -19,6 +20,35 @@ Harness.test({ text : 'SELECT (`customer`.`name` + `customer`.`age`) FROM `customer`', string: 'SELECT (`customer`.`name` + `customer`.`age`) FROM `customer`' }, + mssql: { + text : 'SELECT ([customer].[name] + [customer].[age]) FROM [customer]', + string: 'SELECT ([customer].[name] + [customer].[age]) FROM [customer]' + }, + oracle: { + text : 'SELECT ("customer"."name" + "customer"."age") FROM "customer"', + string: 'SELECT ("customer"."name" + "customer"."age") FROM "customer"' + }, + params: [] +}); + +Harness.test({ + query: customerAlias.select(customerAlias.name_alias.plus(customerAlias.age_alias)), + pg: { + text : 'SELECT ("customer"."name" + "customer"."age") FROM "customer"', + string: 'SELECT ("customer"."name" + "customer"."age") FROM "customer"' + }, + sqlite: { + text : 'SELECT ("customer"."name" + "customer"."age") FROM "customer"', + string: 'SELECT ("customer"."name" + "customer"."age") FROM "customer"' + }, + mysql: { + text : 'SELECT (`customer`.`name` + `customer`.`age`) FROM `customer`', + string: 'SELECT (`customer`.`name` + `customer`.`age`) FROM `customer`' + }, + mssql: { + text : 'SELECT ([customer].[name] + [customer].[age]) FROM [customer]', + string: 'SELECT ([customer].[name] + [customer].[age]) FROM [customer]' + }, params: [] }); @@ -36,6 +66,14 @@ Harness.test({ text : 'SELECT (`post`.`content` + ?) FROM `post` WHERE (`post`.`userId` IN (SELECT `customer`.`id` FROM `customer`))', string: 'SELECT (`post`.`content` + \'!\') FROM `post` WHERE (`post`.`userId` IN (SELECT `customer`.`id` FROM `customer`))' }, + mssql: { + text : 'SELECT ([post].[content] + @1) FROM [post] WHERE ([post].[userId] IN (SELECT [customer].[id] FROM [customer]))', + string: 'SELECT ([post].[content] + \'!\') FROM [post] WHERE ([post].[userId] IN (SELECT [customer].[id] FROM [customer]))' + }, + oracle: { + text : 'SELECT ("post"."content" + :1) FROM "post" WHERE ("post"."userId" IN (SELECT "customer"."id" FROM "customer"))', + string: 'SELECT ("post"."content" + \'!\') FROM "post" WHERE ("post"."userId" IN (SELECT "customer"."id" FROM "customer"))' + }, params: ['!'] }); @@ -53,5 +91,13 @@ Harness.test({ text : 'SELECT ((`post`.`id` + ?) + `post`.`content`) FROM `post` WHERE (`post`.`userId` NOT IN (SELECT `customer`.`id` FROM `customer`))', string: 'SELECT ((`post`.`id` + \': \') + `post`.`content`) FROM `post` WHERE (`post`.`userId` NOT IN (SELECT `customer`.`id` FROM `customer`))' }, + mssql: { + text : 'SELECT (([post].[id] + @1) + [post].[content]) FROM [post] WHERE ([post].[userId] NOT IN (SELECT [customer].[id] FROM [customer]))', + string: 'SELECT (([post].[id] + \': \') + [post].[content]) FROM [post] WHERE ([post].[userId] NOT IN (SELECT [customer].[id] FROM [customer]))' + }, + oracle: { + text : 'SELECT (("post"."id" + :1) + "post"."content") FROM "post" WHERE ("post"."userId" NOT IN (SELECT "customer"."id" FROM "customer"))', + string: 'SELECT (("post"."id" + \': \') + "post"."content") FROM "post" WHERE ("post"."userId" NOT IN (SELECT "customer"."id" FROM "customer"))' + }, params: [': '] }); diff --git a/test/dialects/case-tests.js b/test/dialects/case-tests.js new file mode 100644 index 00000000..f492a4f5 --- /dev/null +++ b/test/dialects/case-tests.js @@ -0,0 +1,222 @@ +'use strict'; + +var Harness = require('./support'); +var customer = Harness.defineCustomerTable(); + +// Check case expression with primary when expressions and else branch. +Harness.test({ + query: customer.select(customer.age.case([true, false], [0, 1], 2)), + pg: { + text : 'SELECT (CASE WHEN $1 THEN $2 WHEN $3 THEN $4 ELSE $5 END) FROM "customer"', + string: 'SELECT (CASE WHEN TRUE THEN 0 WHEN FALSE THEN 1 ELSE 2 END) FROM "customer"' + }, + sqlite: { + text : 'SELECT (CASE WHEN $1 THEN $2 WHEN $3 THEN $4 ELSE $5 END) FROM "customer"', + string: 'SELECT (CASE WHEN 1 THEN 0 WHEN 0 THEN 1 ELSE 2 END) FROM "customer"' + }, + mysql: { + text : 'SELECT (CASE WHEN ? THEN ? WHEN ? THEN ? ELSE ? END) FROM `customer`', + string: 'SELECT (CASE WHEN TRUE THEN 0 WHEN FALSE THEN 1 ELSE 2 END) FROM `customer`' + }, + mssql: { + text : 'SELECT (CASE WHEN 1=1 THEN @1 WHEN 0=1 THEN @2 ELSE @3 END) FROM [customer]', + string: 'SELECT (CASE WHEN 1=1 THEN 0 WHEN 0=1 THEN 1 ELSE 2 END) FROM [customer]', + params: [0, 1, 2] + }, + oracle: { + text : 'SELECT (CASE WHEN 1=1 THEN :1 WHEN 0=1 THEN :2 ELSE :3 END) FROM "customer"', + string: 'SELECT (CASE WHEN 1=1 THEN 0 WHEN 0=1 THEN 1 ELSE 2 END) FROM "customer"', + params: [0, 1, 2] + }, + params: [true, 0, false, 1, 2] +}); + +// Check case expression as a subexpression. +Harness.test({ + query: customer.select(customer.age.plus(customer.age.case([true, false], [0, 1], 2))), + pg: { + text : 'SELECT ("customer"."age" + (CASE WHEN $1 THEN $2 WHEN $3 THEN $4 ELSE $5 END)) FROM "customer"', + string: 'SELECT ("customer"."age" + (CASE WHEN TRUE THEN 0 WHEN FALSE THEN 1 ELSE 2 END)) FROM "customer"' + }, + sqlite: { + text : 'SELECT ("customer"."age" + (CASE WHEN $1 THEN $2 WHEN $3 THEN $4 ELSE $5 END)) FROM "customer"', + string: 'SELECT ("customer"."age" + (CASE WHEN 1 THEN 0 WHEN 0 THEN 1 ELSE 2 END)) FROM "customer"' + }, + mysql: { + text : 'SELECT (`customer`.`age` + (CASE WHEN ? THEN ? WHEN ? THEN ? ELSE ? END)) FROM `customer`', + string: 'SELECT (`customer`.`age` + (CASE WHEN TRUE THEN 0 WHEN FALSE THEN 1 ELSE 2 END)) FROM `customer`' + }, + mssql: { + text : 'SELECT ([customer].[age] + (CASE WHEN 1=1 THEN @1 WHEN 0=1 THEN @2 ELSE @3 END)) FROM [customer]', + string: 'SELECT ([customer].[age] + (CASE WHEN 1=1 THEN 0 WHEN 0=1 THEN 1 ELSE 2 END)) FROM [customer]', + params: [0, 1, 2] + }, + oracle: { + text : 'SELECT ("customer"."age" + (CASE WHEN 1=1 THEN :1 WHEN 0=1 THEN :2 ELSE :3 END)) FROM "customer"', + string: 'SELECT ("customer"."age" + (CASE WHEN 1=1 THEN 0 WHEN 0=1 THEN 1 ELSE 2 END)) FROM "customer"', + params: [0, 1, 2] + }, + params: [true, 0, false, 1, 2] +}); + +// Check case expression as subexpression on the left. +Harness.test({ + query: customer.select(customer.age.case([true, false], [0, 1], 2).plus(3)), + pg: { + text : 'SELECT ((CASE WHEN $1 THEN $2 WHEN $3 THEN $4 ELSE $5 END) + $6) FROM "customer"', + string: 'SELECT ((CASE WHEN TRUE THEN 0 WHEN FALSE THEN 1 ELSE 2 END) + 3) FROM "customer"', + }, + sqlite: { + text : 'SELECT ((CASE WHEN $1 THEN $2 WHEN $3 THEN $4 ELSE $5 END) + $6) FROM "customer"', + string: 'SELECT ((CASE WHEN 1 THEN 0 WHEN 0 THEN 1 ELSE 2 END) + 3) FROM "customer"' + }, + mysql: { + text : 'SELECT ((CASE WHEN ? THEN ? WHEN ? THEN ? ELSE ? END) + ?) FROM `customer`', + string: 'SELECT ((CASE WHEN TRUE THEN 0 WHEN FALSE THEN 1 ELSE 2 END) + 3) FROM `customer`' + }, + mssql: { + text : 'SELECT ((CASE WHEN 1=1 THEN @1 WHEN 0=1 THEN @2 ELSE @3 END) + @4) FROM [customer]', + string: 'SELECT ((CASE WHEN 1=1 THEN 0 WHEN 0=1 THEN 1 ELSE 2 END) + 3) FROM [customer]', + params: [0, 1, 2, 3] + }, + oracle: { + text : 'SELECT ((CASE WHEN 1=1 THEN :1 WHEN 0=1 THEN :2 ELSE :3 END) + :4) FROM "customer"', + string: 'SELECT ((CASE WHEN 1=1 THEN 0 WHEN 0=1 THEN 1 ELSE 2 END) + 3) FROM "customer"', + params: [0, 1, 2, 3] + }, + params: [true, 0, false, 1, 2, 3] +}); + +// Check case expression with primary when expressions and compound else expression. +Harness.test({ + query: customer.select(customer.age.case([true, false], [0, 1], customer.age.between(10, 20))), + pg: { + text : 'SELECT (CASE WHEN $1 THEN $2 WHEN $3 THEN $4 ELSE ("customer"."age" BETWEEN $5 AND $6) END) FROM "customer"', + string: 'SELECT (CASE WHEN TRUE THEN 0 WHEN FALSE THEN 1 ELSE ("customer"."age" BETWEEN 10 AND 20) END) FROM "customer"', + }, + sqlite: { + text : 'SELECT (CASE WHEN $1 THEN $2 WHEN $3 THEN $4 ELSE ("customer"."age" BETWEEN $5 AND $6) END) FROM "customer"', + string: 'SELECT (CASE WHEN 1 THEN 0 WHEN 0 THEN 1 ELSE ("customer"."age" BETWEEN 10 AND 20) END) FROM "customer"' + }, + mysql: { + text : 'SELECT (CASE WHEN ? THEN ? WHEN ? THEN ? ELSE (`customer`.`age` BETWEEN ? AND ?) END) FROM `customer`', + string: 'SELECT (CASE WHEN TRUE THEN 0 WHEN FALSE THEN 1 ELSE (`customer`.`age` BETWEEN 10 AND 20) END) FROM `customer`' + }, + mssql: { + text : 'SELECT (CASE WHEN 1=1 THEN @1 WHEN 0=1 THEN @2 ELSE ([customer].[age] BETWEEN @3 AND @4) END) FROM [customer]', + string: 'SELECT (CASE WHEN 1=1 THEN 0 WHEN 0=1 THEN 1 ELSE ([customer].[age] BETWEEN 10 AND 20) END) FROM [customer]', + params: [0, 1, 10, 20] + }, + oracle: { + text : 'SELECT (CASE WHEN 1=1 THEN :1 WHEN 0=1 THEN :2 ELSE ("customer"."age" BETWEEN :3 AND :4) END) FROM "customer"', + string: 'SELECT (CASE WHEN 1=1 THEN 0 WHEN 0=1 THEN 1 ELSE ("customer"."age" BETWEEN 10 AND 20) END) FROM "customer"', + params: [0, 1, 10, 20] + }, + params: [true, 0, false, 1, 10, 20] +}); + +// Check case expression with primary when expressions without else branch. +Harness.test({ + query: customer.select(customer.age.case([true, false], [0, 1])), + pg: { + text : 'SELECT (CASE WHEN $1 THEN $2 WHEN $3 THEN $4 END) FROM "customer"', + string: 'SELECT (CASE WHEN TRUE THEN 0 WHEN FALSE THEN 1 END) FROM "customer"', + }, + sqlite: { + text : 'SELECT (CASE WHEN $1 THEN $2 WHEN $3 THEN $4 END) FROM "customer"', + string: 'SELECT (CASE WHEN 1 THEN 0 WHEN 0 THEN 1 END) FROM "customer"' + }, + mysql: { + text : 'SELECT (CASE WHEN ? THEN ? WHEN ? THEN ? END) FROM `customer`', + string: 'SELECT (CASE WHEN TRUE THEN 0 WHEN FALSE THEN 1 END) FROM `customer`' + }, + mssql: { + text : 'SELECT (CASE WHEN 1=1 THEN @1 WHEN 0=1 THEN @2 END) FROM [customer]', + string: 'SELECT (CASE WHEN 1=1 THEN 0 WHEN 0=1 THEN 1 END) FROM [customer]', + params: [0, 1] + }, + oracle: { + text : 'SELECT (CASE WHEN 1=1 THEN :1 WHEN 0=1 THEN :2 END) FROM "customer"', + string: 'SELECT (CASE WHEN 1=1 THEN 0 WHEN 0=1 THEN 1 END) FROM "customer"', + params: [0, 1] + }, + params: [true, 0, false, 1] +}); + +// Check case expression with compound when expressions and else branch. +Harness.test({ + query: customer.select(customer.age.case([customer.age.in([10, 20, 30]), customer.age.lte(60)], [0, 1], 2)), + pg: { + text : 'SELECT (CASE WHEN ("customer"."age" IN ($1, $2, $3)) THEN $4 WHEN ("customer"."age" <= $5) THEN $6 ELSE $7 END) FROM "customer"', + string: 'SELECT (CASE WHEN ("customer"."age" IN (10, 20, 30)) THEN 0 WHEN ("customer"."age" <= 60) THEN 1 ELSE 2 END) FROM "customer"' + }, + sqlite: { + text : 'SELECT (CASE WHEN ("customer"."age" IN ($1, $2, $3)) THEN $4 WHEN ("customer"."age" <= $5) THEN $6 ELSE $7 END) FROM "customer"', + string: 'SELECT (CASE WHEN ("customer"."age" IN (10, 20, 30)) THEN 0 WHEN ("customer"."age" <= 60) THEN 1 ELSE 2 END) FROM "customer"' + }, + mysql: { + text : 'SELECT (CASE WHEN (`customer`.`age` IN (?, ?, ?)) THEN ? WHEN (`customer`.`age` <= ?) THEN ? ELSE ? END) FROM `customer`', + string: 'SELECT (CASE WHEN (`customer`.`age` IN (10, 20, 30)) THEN 0 WHEN (`customer`.`age` <= 60) THEN 1 ELSE 2 END) FROM `customer`' + }, + mssql: { + text : 'SELECT (CASE WHEN ([customer].[age] IN (@1, @2, @3)) THEN @4 WHEN ([customer].[age] <= @5) THEN @6 ELSE @7 END) FROM [customer]', + string: 'SELECT (CASE WHEN ([customer].[age] IN (10, 20, 30)) THEN 0 WHEN ([customer].[age] <= 60) THEN 1 ELSE 2 END) FROM [customer]' + }, + oracle: { + text : 'SELECT (CASE WHEN ("customer"."age" IN (:1, :2, :3)) THEN :4 WHEN ("customer"."age" <= :5) THEN :6 ELSE :7 END) FROM "customer"', + string: 'SELECT (CASE WHEN ("customer"."age" IN (10, 20, 30)) THEN 0 WHEN ("customer"."age" <= 60) THEN 1 ELSE 2 END) FROM "customer"' + }, + params: [10, 20, 30, 0, 60, 1, 2] +}); + +// Check case expression without else branch. +Harness.test({ + query: customer.select(customer.age.case([customer.age.in([10, 20, 30]), customer.age.lte(60)], [0, 1])), + pg: { + text : 'SELECT (CASE WHEN ("customer"."age" IN ($1, $2, $3)) THEN $4 WHEN ("customer"."age" <= $5) THEN $6 END) FROM "customer"', + string: 'SELECT (CASE WHEN ("customer"."age" IN (10, 20, 30)) THEN 0 WHEN ("customer"."age" <= 60) THEN 1 END) FROM "customer"' + }, + sqlite: { + text : 'SELECT (CASE WHEN ("customer"."age" IN ($1, $2, $3)) THEN $4 WHEN ("customer"."age" <= $5) THEN $6 END) FROM "customer"', + string: 'SELECT (CASE WHEN ("customer"."age" IN (10, 20, 30)) THEN 0 WHEN ("customer"."age" <= 60) THEN 1 END) FROM "customer"' + }, + mysql: { + text : 'SELECT (CASE WHEN (`customer`.`age` IN (?, ?, ?)) THEN ? WHEN (`customer`.`age` <= ?) THEN ? END) FROM `customer`', + string: 'SELECT (CASE WHEN (`customer`.`age` IN (10, 20, 30)) THEN 0 WHEN (`customer`.`age` <= 60) THEN 1 END) FROM `customer`' + }, + mssql: { + text : 'SELECT (CASE WHEN ([customer].[age] IN (@1, @2, @3)) THEN @4 WHEN ([customer].[age] <= @5) THEN @6 END) FROM [customer]', + string: 'SELECT (CASE WHEN ([customer].[age] IN (10, 20, 30)) THEN 0 WHEN ([customer].[age] <= 60) THEN 1 END) FROM [customer]' + }, + oracle: { + text : 'SELECT (CASE WHEN ("customer"."age" IN (:1, :2, :3)) THEN :4 WHEN ("customer"."age" <= :5) THEN :6 END) FROM "customer"', + string: 'SELECT (CASE WHEN ("customer"."age" IN (10, 20, 30)) THEN 0 WHEN ("customer"."age" <= 60) THEN 1 END) FROM "customer"' + }, + params: [10, 20, 30, 0, 60, 1] +}); + +// Check case expression with compound then expressions. +Harness.test({ + query: customer.select(customer.age.case([customer.age.in([10, 20, 30]), customer.age.lte(60)], [customer.age.plus(5), customer.age.minus(1)])), + pg: { + text : 'SELECT (CASE WHEN ("customer"."age" IN ($1, $2, $3)) THEN ("customer"."age" + $4) WHEN ("customer"."age" <= $5) THEN ("customer"."age" - $6) END) FROM "customer"', + string: 'SELECT (CASE WHEN ("customer"."age" IN (10, 20, 30)) THEN ("customer"."age" + 5) WHEN ("customer"."age" <= 60) THEN ("customer"."age" - 1) END) FROM "customer"' + }, + sqlite: { + text : 'SELECT (CASE WHEN ("customer"."age" IN ($1, $2, $3)) THEN ("customer"."age" + $4) WHEN ("customer"."age" <= $5) THEN ("customer"."age" - $6) END) FROM "customer"', + string: 'SELECT (CASE WHEN ("customer"."age" IN (10, 20, 30)) THEN ("customer"."age" + 5) WHEN ("customer"."age" <= 60) THEN ("customer"."age" - 1) END) FROM "customer"' + }, + mysql: { + text : 'SELECT (CASE WHEN (`customer`.`age` IN (?, ?, ?)) THEN (`customer`.`age` + ?) WHEN (`customer`.`age` <= ?) THEN (`customer`.`age` - ?) END) FROM `customer`', + string: 'SELECT (CASE WHEN (`customer`.`age` IN (10, 20, 30)) THEN (`customer`.`age` + 5) WHEN (`customer`.`age` <= 60) THEN (`customer`.`age` - 1) END) FROM `customer`' + }, + mssql: { + text : 'SELECT (CASE WHEN ([customer].[age] IN (@1, @2, @3)) THEN ([customer].[age] + @4) WHEN ([customer].[age] <= @5) THEN ([customer].[age] - @6) END) FROM [customer]', + string: 'SELECT (CASE WHEN ([customer].[age] IN (10, 20, 30)) THEN ([customer].[age] + 5) WHEN ([customer].[age] <= 60) THEN ([customer].[age] - 1) END) FROM [customer]' + }, + oracle: { + text : 'SELECT (CASE WHEN ("customer"."age" IN (:1, :2, :3)) THEN ("customer"."age" + :4) WHEN ("customer"."age" <= :5) THEN ("customer"."age" - :6) END) FROM "customer"', + string: 'SELECT (CASE WHEN ("customer"."age" IN (10, 20, 30)) THEN ("customer"."age" + 5) WHEN ("customer"."age" <= 60) THEN ("customer"."age" - 1) END) FROM "customer"' + }, + params: [10, 20, 30, 5, 60, 1] +}); diff --git a/test/dialects/cast-tests.js b/test/dialects/cast-tests.js new file mode 100644 index 00000000..2295d720 --- /dev/null +++ b/test/dialects/cast-tests.js @@ -0,0 +1,314 @@ +'use strict'; + +var Harness = require('./support'); +var customer = Harness.defineCustomerTable(); +var customerAlias = Harness.defineCustomerAliasTable(); + +// Cast columns. +Harness.test({ + query: customer.select(customer.age.cast('int')), + pg: { + text : 'SELECT CAST("customer"."age" AS int) FROM "customer"', + string: 'SELECT CAST("customer"."age" AS int) FROM "customer"' + }, + sqlite: { + text : 'SELECT CAST("customer"."age" AS int) FROM "customer"', + string: 'SELECT CAST("customer"."age" AS int) FROM "customer"' + }, + mysql: { + text : 'SELECT CAST(`customer`.`age` AS int) FROM `customer`', + string: 'SELECT CAST(`customer`.`age` AS int) FROM `customer`' + }, + mssql: { + text : 'SELECT CAST([customer].[age] AS int) FROM [customer]', + string: 'SELECT CAST([customer].[age] AS int) FROM [customer]' + }, + oracle: { + text : 'SELECT CAST("customer"."age" AS int) FROM "customer"', + string: 'SELECT CAST("customer"."age" AS int) FROM "customer"' + }, + params: [] +}); + +Harness.test({ + query: customer.select(customer.name.cast('varchar(10)')), + pg: { + text : 'SELECT CAST("customer"."name" AS varchar(10)) FROM "customer"', + string: 'SELECT CAST("customer"."name" AS varchar(10)) FROM "customer"' + }, + sqlite: { + text : 'SELECT CAST("customer"."name" AS varchar(10)) FROM "customer"', + string: 'SELECT CAST("customer"."name" AS varchar(10)) FROM "customer"' + }, + mysql: { + text : 'SELECT CAST(`customer`.`name` AS varchar(10)) FROM `customer`', + string: 'SELECT CAST(`customer`.`name` AS varchar(10)) FROM `customer`' + }, + mssql: { + text : 'SELECT CAST([customer].[name] AS varchar(10)) FROM [customer]', + string: 'SELECT CAST([customer].[name] AS varchar(10)) FROM [customer]' + }, + oracle: { + text : 'SELECT CAST("customer"."name" AS varchar(10)) FROM "customer"', + string: 'SELECT CAST("customer"."name" AS varchar(10)) FROM "customer"' + }, + params: [] +}); + +// Cast binary expressions. +Harness.test({ + query: customer.select(customer.name.plus(customer.age).cast('varchar(15)')), + pg: { + text : 'SELECT CAST(("customer"."name" + "customer"."age") AS varchar(15)) FROM "customer"', + string: 'SELECT CAST(("customer"."name" + "customer"."age") AS varchar(15)) FROM "customer"' + }, + sqlite: { + text : 'SELECT CAST(("customer"."name" + "customer"."age") AS varchar(15)) FROM "customer"', + string: 'SELECT CAST(("customer"."name" + "customer"."age") AS varchar(15)) FROM "customer"' + }, + mysql: { + text : 'SELECT CAST((`customer`.`name` + `customer`.`age`) AS varchar(15)) FROM `customer`', + string: 'SELECT CAST((`customer`.`name` + `customer`.`age`) AS varchar(15)) FROM `customer`' + }, + mssql: { + text : 'SELECT CAST(([customer].[name] + [customer].[age]) AS varchar(15)) FROM [customer]', + string: 'SELECT CAST(([customer].[name] + [customer].[age]) AS varchar(15)) FROM [customer]' + }, + oracle: { + text : 'SELECT CAST(("customer"."name" + "customer"."age") AS varchar(15)) FROM "customer"', + string: 'SELECT CAST(("customer"."name" + "customer"."age") AS varchar(15)) FROM "customer"' + }, + params: [] +}); + +// Cast cast expressions. +Harness.test({ + query: customer.select(customer.name.cast('varchar(15)').cast('varchar(10)')), + pg: { + text : 'SELECT CAST(CAST("customer"."name" AS varchar(15)) AS varchar(10)) FROM "customer"', + string: 'SELECT CAST(CAST("customer"."name" AS varchar(15)) AS varchar(10)) FROM "customer"' + }, + sqlite: { + text : 'SELECT CAST(CAST("customer"."name" AS varchar(15)) AS varchar(10)) FROM "customer"', + string: 'SELECT CAST(CAST("customer"."name" AS varchar(15)) AS varchar(10)) FROM "customer"' + }, + mysql: { + text : 'SELECT CAST(CAST(`customer`.`name` AS varchar(15)) AS varchar(10)) FROM `customer`', + string: 'SELECT CAST(CAST(`customer`.`name` AS varchar(15)) AS varchar(10)) FROM `customer`' + }, + mssql: { + text : 'SELECT CAST(CAST([customer].[name] AS varchar(15)) AS varchar(10)) FROM [customer]', + string: 'SELECT CAST(CAST([customer].[name] AS varchar(15)) AS varchar(10)) FROM [customer]' + }, + oracle: { + text : 'SELECT CAST(CAST("customer"."name" AS varchar(15)) AS varchar(10)) FROM "customer"', + string: 'SELECT CAST(CAST("customer"."name" AS varchar(15)) AS varchar(10)) FROM "customer"' + }, + params: [] +}); + +// Cast in WHERE. +Harness.test({ + query: customer.select(customer.name).where(customer.age.cast('int').plus(100).equals(150)), + pg: { + text : 'SELECT "customer"."name" FROM "customer" WHERE ((CAST("customer"."age" AS int) + $1) = $2)', + string: 'SELECT "customer"."name" FROM "customer" WHERE ((CAST("customer"."age" AS int) + 100) = 150)' + }, + sqlite: { + text : 'SELECT "customer"."name" FROM "customer" WHERE ((CAST("customer"."age" AS int) + $1) = $2)', + string: 'SELECT "customer"."name" FROM "customer" WHERE ((CAST("customer"."age" AS int) + 100) = 150)' + }, + mysql: { + text : 'SELECT `customer`.`name` FROM `customer` WHERE ((CAST(`customer`.`age` AS int) + ?) = ?)', + string: 'SELECT `customer`.`name` FROM `customer` WHERE ((CAST(`customer`.`age` AS int) + 100) = 150)' + }, + mssql: { + text : 'SELECT [customer].[name] FROM [customer] WHERE ((CAST([customer].[age] AS int) + @1) = @2)', + string: 'SELECT [customer].[name] FROM [customer] WHERE ((CAST([customer].[age] AS int) + 100) = 150)' + }, + oracle: { + text : 'SELECT "customer"."name" FROM "customer" WHERE ((CAST("customer"."age" AS int) + :1) = :2)', + string: 'SELECT "customer"."name" FROM "customer" WHERE ((CAST("customer"."age" AS int) + 100) = 150)' + }, + params: [100, 150] +}); + +// Alias cast. +Harness.test({ + query: customer.select(customer.age.cast('int').as('age_int')), + pg: { + text : 'SELECT CAST("customer"."age" AS int) AS "age_int" FROM "customer"', + string: 'SELECT CAST("customer"."age" AS int) AS "age_int" FROM "customer"' + }, + sqlite: { + text : 'SELECT CAST("customer"."age" AS int) AS "age_int" FROM "customer"', + string: 'SELECT CAST("customer"."age" AS int) AS "age_int" FROM "customer"' + }, + mysql: { + text : 'SELECT CAST(`customer`.`age` AS int) AS `age_int` FROM `customer`', + string: 'SELECT CAST(`customer`.`age` AS int) AS `age_int` FROM `customer`' + }, + mssql: { + text : 'SELECT CAST([customer].[age] AS int) AS [age_int] FROM [customer]', + string: 'SELECT CAST([customer].[age] AS int) AS [age_int] FROM [customer]' + }, + oracle: { + text : 'SELECT CAST("customer"."age" AS int) "age_int" FROM "customer"', + string: 'SELECT CAST("customer"."age" AS int) "age_int" FROM "customer"' + }, + params: [] +}); + +// Cast Aliased columns. +Harness.test({ + query: customerAlias.select(customerAlias.age_alias.cast('int')), + pg: { + text : 'SELECT CAST("customer"."age" AS int) FROM "customer"', + string: 'SELECT CAST("customer"."age" AS int) FROM "customer"' + }, + sqlite: { + text : 'SELECT CAST("customer"."age" AS int) FROM "customer"', + string: 'SELECT CAST("customer"."age" AS int) FROM "customer"' + }, + mysql: { + text : 'SELECT CAST(`customer`.`age` AS int) FROM `customer`', + string: 'SELECT CAST(`customer`.`age` AS int) FROM `customer`' + }, + mssql: { + text : 'SELECT CAST([customer].[age] AS int) FROM [customer]', + string: 'SELECT CAST([customer].[age] AS int) FROM [customer]' + }, + oracle: { + text : 'SELECT CAST("customer"."age" AS int) FROM "customer"', + string: 'SELECT CAST("customer"."age" AS int) FROM "customer"' + }, + params: [] +}); + +Harness.test({ + query: customerAlias.select(customerAlias.name_alias.cast('varchar(10)')), + pg: { + text : 'SELECT CAST("customer"."name" AS varchar(10)) FROM "customer"', + string: 'SELECT CAST("customer"."name" AS varchar(10)) FROM "customer"' + }, + sqlite: { + text : 'SELECT CAST("customer"."name" AS varchar(10)) FROM "customer"', + string: 'SELECT CAST("customer"."name" AS varchar(10)) FROM "customer"' + }, + mysql: { + text : 'SELECT CAST(`customer`.`name` AS varchar(10)) FROM `customer`', + string: 'SELECT CAST(`customer`.`name` AS varchar(10)) FROM `customer`' + }, + mssql: { + text : 'SELECT CAST([customer].[name] AS varchar(10)) FROM [customer]', + string: 'SELECT CAST([customer].[name] AS varchar(10)) FROM [customer]' + }, + oracle: { + text : 'SELECT CAST("customer"."name" AS varchar(10)) FROM "customer"', + string: 'SELECT CAST("customer"."name" AS varchar(10)) FROM "customer"' + }, + params: [] +}); +// Cast binary expressions for Aliased Column. +Harness.test({ + query: customerAlias.select(customerAlias.name_alias.plus(customerAlias.age_alias).cast('varchar(15)')), + pg: { + text : 'SELECT CAST(("customer"."name" + "customer"."age") AS varchar(15)) FROM "customer"', + string: 'SELECT CAST(("customer"."name" + "customer"."age") AS varchar(15)) FROM "customer"' + }, + sqlite: { + text : 'SELECT CAST(("customer"."name" + "customer"."age") AS varchar(15)) FROM "customer"', + string: 'SELECT CAST(("customer"."name" + "customer"."age") AS varchar(15)) FROM "customer"' + }, + mysql: { + text : 'SELECT CAST((`customer`.`name` + `customer`.`age`) AS varchar(15)) FROM `customer`', + string: 'SELECT CAST((`customer`.`name` + `customer`.`age`) AS varchar(15)) FROM `customer`' + }, + mssql: { + text : 'SELECT CAST(([customer].[name] + [customer].[age]) AS varchar(15)) FROM [customer]', + string: 'SELECT CAST(([customer].[name] + [customer].[age]) AS varchar(15)) FROM [customer]' + }, + oracle: { + text : 'SELECT CAST(("customer"."name" + "customer"."age") AS varchar(15)) FROM "customer"', + string: 'SELECT CAST(("customer"."name" + "customer"."age") AS varchar(15)) FROM "customer"' + }, + params: [] +}); + +// Cast cast expressions for aliased columns. +Harness.test({ + query: customerAlias.select(customerAlias.name_alias.cast('varchar(15)').cast('varchar(10)')), + pg: { + text : 'SELECT CAST(CAST("customer"."name" AS varchar(15)) AS varchar(10)) FROM "customer"', + string: 'SELECT CAST(CAST("customer"."name" AS varchar(15)) AS varchar(10)) FROM "customer"' + }, + sqlite: { + text : 'SELECT CAST(CAST("customer"."name" AS varchar(15)) AS varchar(10)) FROM "customer"', + string: 'SELECT CAST(CAST("customer"."name" AS varchar(15)) AS varchar(10)) FROM "customer"' + }, + mysql: { + text : 'SELECT CAST(CAST(`customer`.`name` AS varchar(15)) AS varchar(10)) FROM `customer`', + string: 'SELECT CAST(CAST(`customer`.`name` AS varchar(15)) AS varchar(10)) FROM `customer`' + }, + mssql: { + text : 'SELECT CAST(CAST([customer].[name] AS varchar(15)) AS varchar(10)) FROM [customer]', + string: 'SELECT CAST(CAST([customer].[name] AS varchar(15)) AS varchar(10)) FROM [customer]' + }, + oracle: { + text : 'SELECT CAST(CAST("customer"."name" AS varchar(15)) AS varchar(10)) FROM "customer"', + string: 'SELECT CAST(CAST("customer"."name" AS varchar(15)) AS varchar(10)) FROM "customer"' + }, + params: [] +}); + +// Cast in WHERE for aliased columns. +Harness.test({ + query: customerAlias.select(customerAlias.name_alias).where(customerAlias.age_alias.cast('int').plus(100).equals(150)), + pg: { + text : 'SELECT "customer"."name" AS "name_alias" FROM "customer" WHERE ((CAST("customer"."age" AS int) + $1) = $2)', + string: 'SELECT "customer"."name" AS "name_alias" FROM "customer" WHERE ((CAST("customer"."age" AS int) + 100) = 150)' + }, + sqlite: { + text : 'SELECT "customer"."name" AS "name_alias" FROM "customer" WHERE ((CAST("customer"."age" AS int) + $1) = $2)', + string: 'SELECT "customer"."name" AS "name_alias" FROM "customer" WHERE ((CAST("customer"."age" AS int) + 100) = 150)' + }, + mysql: { + text : 'SELECT `customer`.`name` AS `name_alias` FROM `customer` WHERE ((CAST(`customer`.`age` AS int) + ?) = ?)', + string: 'SELECT `customer`.`name` AS `name_alias` FROM `customer` WHERE ((CAST(`customer`.`age` AS int) + 100) = 150)' + }, + mssql: { + text : 'SELECT [customer].[name] AS [name_alias] FROM [customer] WHERE ((CAST([customer].[age] AS int) + @1) = @2)', + string: 'SELECT [customer].[name] AS [name_alias] FROM [customer] WHERE ((CAST([customer].[age] AS int) + 100) = 150)' + }, + oracle: { + text : 'SELECT "customer"."name" "name_alias" FROM "customer" WHERE ((CAST("customer"."age" AS int) + :1) = :2)', + string: 'SELECT "customer"."name" "name_alias" FROM "customer" WHERE ((CAST("customer"."age" AS int) + 100) = 150)' + }, + params: [100, 150] +}); + +// Alias cast. +Harness.test({ + query: customerAlias.select(customerAlias.age_alias.cast('int').as('age_int')), + pg: { + text : 'SELECT CAST("customer"."age" AS int) AS "age_int" FROM "customer"', + string: 'SELECT CAST("customer"."age" AS int) AS "age_int" FROM "customer"' + }, + sqlite: { + text : 'SELECT CAST("customer"."age" AS int) AS "age_int" FROM "customer"', + string: 'SELECT CAST("customer"."age" AS int) AS "age_int" FROM "customer"' + }, + mysql: { + text : 'SELECT CAST(`customer`.`age` AS int) AS `age_int` FROM `customer`', + string: 'SELECT CAST(`customer`.`age` AS int) AS `age_int` FROM `customer`' + }, + mssql: { + text : 'SELECT CAST([customer].[age] AS int) AS [age_int] FROM [customer]', + string: 'SELECT CAST([customer].[age] AS int) AS [age_int] FROM [customer]' + }, + oracle: { + text : 'SELECT CAST("customer"."age" AS int) "age_int" FROM "customer"', + string: 'SELECT CAST("customer"."age" AS int) "age_int" FROM "customer"' + }, + params: [] +}); diff --git a/test/dialects/clause-ordering-tests.js b/test/dialects/clause-ordering-tests.js index 8d957471..83d2d28e 100644 --- a/test/dialects/clause-ordering-tests.js +++ b/test/dialects/clause-ordering-tests.js @@ -9,7 +9,7 @@ Harness.test({ query: user.from(user.join(post).on(user.id.equals(post.userId))).select(user.name, post.content), pg: { text : 'SELECT "user"."name", "post"."content" FROM "user" INNER JOIN "post" ON ("user"."id" = "post"."userId")', - string: 'SELECT "user"."name", "post"."content" FROM "user" INNER JOIN "post" ON ("user"."id" = "post"."userId")', + string: 'SELECT "user"."name", "post"."content" FROM "user" INNER JOIN "post" ON ("user"."id" = "post"."userId")' }, sqlite: { text : 'SELECT "user"."name", "post"."content" FROM "user" INNER JOIN "post" ON ("user"."id" = "post"."userId")', @@ -19,6 +19,15 @@ Harness.test({ text : 'SELECT `user`.`name`, `post`.`content` FROM `user` INNER JOIN `post` ON (`user`.`id` = `post`.`userId`)', string: 'SELECT `user`.`name`, `post`.`content` FROM `user` INNER JOIN `post` ON (`user`.`id` = `post`.`userId`)' }, + mssql: { + text : 'SELECT [user].[name], [post].[content] FROM [user] INNER JOIN [post] ON ([user].[id] = [post].[userId])', + string: 'SELECT [user].[name], [post].[content] FROM [user] INNER JOIN [post] ON ([user].[id] = [post].[userId])' + }, + oracle: { + text : 'SELECT "user"."name", "post"."content" FROM "user" INNER JOIN "post" ON ("user"."id" = "post"."userId")', + string: 'SELECT "user"."name", "post"."content" FROM "user" INNER JOIN "post" ON ("user"."id" = "post"."userId")' + }, + params: [] }); // WHERE - FROM - SELECT @@ -38,6 +47,14 @@ Harness.test({ text : 'SELECT `user`.`id` FROM `user` WHERE (`user`.`name` = ?)', string: 'SELECT `user`.`id` FROM `user` WHERE (`user`.`name` = \'\')' }, + mssql: { + text : 'SELECT [user].[id] FROM [user] WHERE ([user].[name] = @1)', + string: 'SELECT [user].[id] FROM [user] WHERE ([user].[name] = \'\')' + }, + oracle: { + text : 'SELECT "user"."id" FROM "user" WHERE ("user"."name" = :1)', + string: 'SELECT "user"."id" FROM "user" WHERE ("user"."name" = \'\')' + }, params: [''] }); @@ -61,6 +78,14 @@ Harness.test({ text : 'SELECT `user`.`name`, `post`.`content` FROM `user` INNER JOIN `post` ON (`user`.`id` = `post`.`userId`) WHERE (`user`.`name` = ?)', string: 'SELECT `user`.`name`, `post`.`content` FROM `user` INNER JOIN `post` ON (`user`.`id` = `post`.`userId`) WHERE (`user`.`name` = \'\')' }, + mssql: { + text : 'SELECT [user].[name], [post].[content] FROM [user] INNER JOIN [post] ON ([user].[id] = [post].[userId]) WHERE ([user].[name] = @1)', + string: 'SELECT [user].[name], [post].[content] FROM [user] INNER JOIN [post] ON ([user].[id] = [post].[userId]) WHERE ([user].[name] = \'\')' + }, + oracle: { + text : 'SELECT "user"."name", "post"."content" FROM "user" INNER JOIN "post" ON ("user"."id" = "post"."userId") WHERE ("user"."name" = :1)', + string: 'SELECT "user"."name", "post"."content" FROM "user" INNER JOIN "post" ON ("user"."id" = "post"."userId") WHERE ("user"."name" = \'\')' + }, params: [''] }); @@ -81,5 +106,13 @@ Harness.test({ text : 'SELECT `user`.`id` FROM `user` WHERE (`user`.`name` = ?)', string: 'SELECT `user`.`id` FROM `user` WHERE (`user`.`name` = \'\')' }, + mssql: { + text : 'SELECT [user].[id] FROM [user] WHERE ([user].[name] = @1)', + string: 'SELECT [user].[id] FROM [user] WHERE ([user].[name] = \'\')' + }, + oracle: { + text : 'SELECT "user"."id" FROM "user" WHERE ("user"."name" = :1)', + string: 'SELECT "user"."id" FROM "user" WHERE ("user"."name" = \'\')' + }, params: [''] }); diff --git a/test/dialects/create-table-tests.js b/test/dialects/create-table-tests.js index df2e1a27..b1150617 100644 --- a/test/dialects/create-table-tests.js +++ b/test/dialects/create-table-tests.js @@ -29,6 +29,14 @@ Harness.test({ text : 'CREATE TABLE `group` (`id` varchar(100), `user_id` varchar(100))', string: 'CREATE TABLE `group` (`id` varchar(100), `user_id` varchar(100))' }, + mssql: { + text : 'CREATE TABLE [group] ([id] varchar(100), [user_id] varchar(100))', + string: 'CREATE TABLE [group] ([id] varchar(100), [user_id] varchar(100))' + }, + oracle: { + text : 'CREATE TABLE "group" ("id" varchar(100), "user_id" varchar(100))', + string: 'CREATE TABLE "group" ("id" varchar(100), "user_id" varchar(100))' + }, params: [] }); @@ -46,6 +54,14 @@ Harness.test({ text : 'CREATE TABLE IF NOT EXISTS `group` (`id` varchar(100), `user_id` varchar(100))', string: 'CREATE TABLE IF NOT EXISTS `group` (`id` varchar(100), `user_id` varchar(100))' }, + mssql: { + text : 'IF NOT EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = \'group\') BEGIN CREATE TABLE [group] ([id] varchar(100), [user_id] varchar(100)) END', + string: 'IF NOT EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = \'group\') BEGIN CREATE TABLE [group] ([id] varchar(100), [user_id] varchar(100)) END' + }, + oracle: { + text : 'BEGIN EXECUTE IMMEDIATE \'CREATE TABLE "group" ("id" varchar(100), "user_id" varchar(100))\'; EXCEPTION WHEN OTHERS THEN IF SQLCODE != -955 THEN RAISE; END IF; END;', + string: 'BEGIN EXECUTE IMMEDIATE \'CREATE TABLE "group" ("id" varchar(100), "user_id" varchar(100))\'; EXCEPTION WHEN OTHERS THEN IF SQLCODE != -955 THEN RAISE; END IF; END;' + }, params: [] }); @@ -70,6 +86,14 @@ Harness.test({ mysql: { text : 'CREATE TABLE `user` (`id` varchar(100)) ENGINE=InnoDB', string: 'CREATE TABLE `user` (`id` varchar(100)) ENGINE=InnoDB' + }, + mssql: { + text : 'CREATE TABLE [user] ([id] varchar(100))', + string: 'CREATE TABLE [user] ([id] varchar(100))' + }, + oracle: { + text : 'CREATE TABLE "user" ("id" varchar(100))', + string: 'CREATE TABLE "user" ("id" varchar(100))' } }); @@ -94,6 +118,14 @@ Harness.test({ mysql: { text : 'CREATE TABLE `user` (`id` varchar(100)) DEFAULT CHARSET=latin1', string: 'CREATE TABLE `user` (`id` varchar(100)) DEFAULT CHARSET=latin1' + }, + mssql: { + text : 'CREATE TABLE [user] ([id] varchar(100))', + string: 'CREATE TABLE [user] ([id] varchar(100))' + }, + oracle: { + text : 'CREATE TABLE "user" ("id" varchar(100))', + string: 'CREATE TABLE "user" ("id" varchar(100))' } }); @@ -119,5 +151,611 @@ Harness.test({ mysql: { text : 'CREATE TABLE `user` (`id` varchar(100)) ENGINE=MyISAM DEFAULT CHARSET=latin1', string: 'CREATE TABLE `user` (`id` varchar(100)) ENGINE=MyISAM DEFAULT CHARSET=latin1' + }, + mssql: { + text : 'CREATE TABLE [user] ([id] varchar(100))', + string: 'CREATE TABLE [user] ([id] varchar(100))' + }, + oracle: { + text : 'CREATE TABLE "user" ("id" varchar(100))', + string: 'CREATE TABLE "user" ("id" varchar(100))' + } +}); + +Harness.test({ + query: Table.define({ + name: 'user', + columns: [{ + name: 'id', + dataType: 'int', + primaryKey: true + }] + }).create(), + pg: { + text : 'CREATE TABLE "user" ("id" int PRIMARY KEY)', + string: 'CREATE TABLE "user" ("id" int PRIMARY KEY)' + }, + sqlite: { + text : 'CREATE TABLE "user" ("id" int PRIMARY KEY)', + string: 'CREATE TABLE "user" ("id" int PRIMARY KEY)' + }, + mysql: { + text : 'CREATE TABLE `user` (`id` int PRIMARY KEY)', + string: 'CREATE TABLE `user` (`id` int PRIMARY KEY)' + }, + mssql: { + text : 'CREATE TABLE [user] ([id] int PRIMARY KEY)', + string: 'CREATE TABLE [user] ([id] int PRIMARY KEY)' + }, + oracle: { + text : 'CREATE TABLE "user" ("id" int PRIMARY KEY)', + string: 'CREATE TABLE "user" ("id" int PRIMARY KEY)' + } +}); + +Harness.test({ + query: Table.define({ + name: 'user', + columns: [{ + name: 'id', + dataType: 'int', + notNull: true + }] + }).create(), + pg: { + text : 'CREATE TABLE "user" ("id" int NOT NULL)', + string: 'CREATE TABLE "user" ("id" int NOT NULL)' + }, + sqlite: { + text : 'CREATE TABLE "user" ("id" int NOT NULL)', + string: 'CREATE TABLE "user" ("id" int NOT NULL)' + }, + mysql: { + text : 'CREATE TABLE `user` (`id` int NOT NULL)', + string: 'CREATE TABLE `user` (`id` int NOT NULL)' + }, + oracle: { + text : 'CREATE TABLE "user" ("id" int NOT NULL)', + string: 'CREATE TABLE "user" ("id" int NOT NULL)' + } +}); + +Harness.test({ + query: Table.define({ + name: 'user', + columns: [{ + name: 'id', + dataType: 'int', + primaryKey: true, + notNull: true + }] + }).create(), + pg: { + text : 'CREATE TABLE "user" ("id" int PRIMARY KEY)', + string: 'CREATE TABLE "user" ("id" int PRIMARY KEY)' + }, + sqlite: { + text : 'CREATE TABLE "user" ("id" int PRIMARY KEY)', + string: 'CREATE TABLE "user" ("id" int PRIMARY KEY)' + }, + mysql: { + text : 'CREATE TABLE `user` (`id` int PRIMARY KEY)', + string: 'CREATE TABLE `user` (`id` int PRIMARY KEY)' + }, + oracle: { + text : 'CREATE TABLE "user" ("id" int PRIMARY KEY)', + string: 'CREATE TABLE "user" ("id" int PRIMARY KEY)' + } +}); + +Harness.test({ + query: Table.define({ + name: 'user', + columns: [{ + name: 'id', + dataType: 'int', + primaryKey: true, + notNull: true + }, { + name: 'posts', + dataType: 'int', + notNull: true, + defaultValue: 0 + }] + }).create(), + pg: { + text : 'CREATE TABLE "user" ("id" int PRIMARY KEY, "posts" int NOT NULL DEFAULT 0)', + string: 'CREATE TABLE "user" ("id" int PRIMARY KEY, "posts" int NOT NULL DEFAULT 0)' + }, + sqlite: { + text : 'CREATE TABLE "user" ("id" int PRIMARY KEY, "posts" int NOT NULL DEFAULT 0)', + string: 'CREATE TABLE "user" ("id" int PRIMARY KEY, "posts" int NOT NULL DEFAULT 0)' + }, + mysql: { + text : 'CREATE TABLE `user` (`id` int PRIMARY KEY, `posts` int NOT NULL DEFAULT 0)', + string: 'CREATE TABLE `user` (`id` int PRIMARY KEY, `posts` int NOT NULL DEFAULT 0)' + }, + oracle: { + text : 'CREATE TABLE "user" ("id" int PRIMARY KEY, "posts" int NOT NULL DEFAULT 0)', + string: 'CREATE TABLE "user" ("id" int PRIMARY KEY, "posts" int NOT NULL DEFAULT 0)' + } +}); + +Harness.test({ + query: Table.define({ + name: 'post', + columns: [{ + name: 'userId', + dataType: 'int', + references: { + table: 'user', + column: 'id', + onDelete: 'restrict', + onUpdate: 'set null' + } + }] + }).create(), + pg: { + text : 'CREATE TABLE "post" ("userId" int REFERENCES "user"("id") ON DELETE RESTRICT ON UPDATE SET NULL)', + string: 'CREATE TABLE "post" ("userId" int REFERENCES "user"("id") ON DELETE RESTRICT ON UPDATE SET NULL)' + }, + sqlite: { + text : 'CREATE TABLE "post" ("userId" int REFERENCES "user"("id") ON DELETE RESTRICT ON UPDATE SET NULL)', + string: 'CREATE TABLE "post" ("userId" int REFERENCES "user"("id") ON DELETE RESTRICT ON UPDATE SET NULL)' + }, + mysql: { + text : 'CREATE TABLE `post` (`userId` int REFERENCES `user`(`id`) ON DELETE RESTRICT ON UPDATE SET NULL)', + string: 'CREATE TABLE `post` (`userId` int REFERENCES `user`(`id`) ON DELETE RESTRICT ON UPDATE SET NULL)' + }, + oracle: { + text : 'CREATE TABLE "post" ("userId" int REFERENCES "user"("id") ON DELETE RESTRICT ON UPDATE SET NULL)', + string: 'CREATE TABLE "post" ("userId" int REFERENCES "user"("id") ON DELETE RESTRICT ON UPDATE SET NULL)' + }, + params: [] +}); + +Harness.test({ + query: Table.define({ + name: 'picture', + columns: [{ + name: 'userId', + dataType: 'int', + references: { + table: 'user', + column: 'id' + } + }, { + name: 'caption', + dataType: 'varchar(100)', + references: {} + }] + }).create(), + pg: { + text : 'CREATE TABLE "picture" ("userId" int REFERENCES "user"("id"), "caption" varchar(100))', + string: 'CREATE TABLE "picture" ("userId" int REFERENCES "user"("id"), "caption" varchar(100))' + }, + sqlite: { + text : 'CREATE TABLE "picture" ("userId" int REFERENCES "user"("id"), "caption" varchar(100))', + string: 'CREATE TABLE "picture" ("userId" int REFERENCES "user"("id"), "caption" varchar(100))' + }, + mysql: { + text : 'CREATE TABLE `picture` (`userId` int REFERENCES `user`(`id`), `caption` varchar(100))', + string: 'CREATE TABLE `picture` (`userId` int REFERENCES `user`(`id`), `caption` varchar(100))' + }, + oracle: { + text : 'CREATE TABLE "picture" ("userId" int REFERENCES "user"("id"), "caption" varchar(100))', + string: 'CREATE TABLE "picture" ("userId" int REFERENCES "user"("id"), "caption" varchar(100))' + }, + params: [] +}); + +Harness.test({ + query: Table.define({ + name: 'picture', + columns: [{ + name: 'userId', + dataType: 'int', + references: { + table: 'user', + column: 'id', + onDelete: 'cascade' + } + }] + }).create(), + pg: { + text : 'CREATE TABLE "picture" ("userId" int REFERENCES "user"("id") ON DELETE CASCADE)', + string: 'CREATE TABLE "picture" ("userId" int REFERENCES "user"("id") ON DELETE CASCADE)' + }, + sqlite: { + text : 'CREATE TABLE "picture" ("userId" int REFERENCES "user"("id") ON DELETE CASCADE)', + string: 'CREATE TABLE "picture" ("userId" int REFERENCES "user"("id") ON DELETE CASCADE)' + }, + mysql: { + text : 'CREATE TABLE `picture` (`userId` int REFERENCES `user`(`id`) ON DELETE CASCADE)', + string: 'CREATE TABLE `picture` (`userId` int REFERENCES `user`(`id`) ON DELETE CASCADE)' + }, + oracle: { + text : 'CREATE TABLE "picture" ("userId" int REFERENCES "user"("id") ON DELETE CASCADE)', + string: 'CREATE TABLE "picture" ("userId" int REFERENCES "user"("id") ON DELETE CASCADE)' + }, + params: [] +}); + +Harness.test({ + query: Table.define({ + name: 'post', + columns: [{ + name: 'userId', + dataType: 'int', + references: 'user' + }] + }).create(), + pg: { + text : 'references is not a object for column userId (REFERENCES statements within CREATE TABLE and ADD COLUMN statements require refrences to be expressed as an object)', + throws: true + }, + sqlite: { + text : 'references is not a object for column userId (REFERENCES statements within CREATE TABLE and ADD COLUMN statements require refrences to be expressed as an object)', + throws: true + }, + mysql: { + text : 'references is not a object for column userId (REFERENCES statements within CREATE TABLE and ADD COLUMN statements require refrences to be expressed as an object)', + throws: true + }, + oracle: { + text : 'references is not a object for column userId (REFERENCES statements within CREATE TABLE and ADD COLUMN statements require refrences to be expressed as an object)', + throws: true + }, + params: [] +}); + +Harness.test({ + query: Table.define({ + name: 'membership', + columns: { + group_id: { dataType: 'int', primaryKey: true}, + user_id: { dataType: 'int', primaryKey: true}, + desc: { dataType: 'varchar'} + } + }).create(), + pg: { + text : 'CREATE TABLE "membership" ("group_id" int, "user_id" int, "desc" varchar, PRIMARY KEY ("group_id", "user_id"))', + string: 'CREATE TABLE "membership" ("group_id" int, "user_id" int, "desc" varchar, PRIMARY KEY ("group_id", "user_id"))', + }, + sqlite: { + text : 'CREATE TABLE "membership" ("group_id" int, "user_id" int, "desc" varchar, PRIMARY KEY ("group_id", "user_id"))', + string: 'CREATE TABLE "membership" ("group_id" int, "user_id" int, "desc" varchar, PRIMARY KEY ("group_id", "user_id"))', + }, + mysql: { + text : 'CREATE TABLE `membership` (`group_id` int, `user_id` int, `desc` varchar, PRIMARY KEY (`group_id`, `user_id`))', + string: 'CREATE TABLE `membership` (`group_id` int, `user_id` int, `desc` varchar, PRIMARY KEY (`group_id`, `user_id`))', } }); + +// TEMPORARY TABLE TESTS + +// This tests explicitly setting the isTemporary flag to false, as opposed to all the test above here which have it +// as undefined. +Harness.test({ + query: Table.define({ + name: 'post', + columns: [{ + name: 'id', + dataType: 'int' + }], + isTemporary:false + }).create(), + pg: { + text : 'CREATE TABLE "post" ("id" int)', + string: 'CREATE TABLE "post" ("id" int)' + }, + sqlite: { + text : 'CREATE TABLE "post" ("id" int)', + string: 'CREATE TABLE "post" ("id" int)' + }, + mysql: { + text : 'CREATE TABLE `post` (`id` int)', + string: 'CREATE TABLE `post` (`id` int)' + }, + mssql: { + text : 'CREATE TABLE [post] ([id] int)', + string: 'CREATE TABLE [post] ([id] int)' + }, + oracle: { + text : 'CREATE TABLE "post" ("id" int)', + string: 'CREATE TABLE "post" ("id" int)' + }, + params: [] +}); + +Harness.test({ + query: Table.define({ + name: 'post', + columns: [{ + name: 'id', + dataType: 'int' + }], + isTemporary:true + }).create(), + pg: { + text : 'CREATE TEMPORARY TABLE "post" ("id" int)', + string: 'CREATE TEMPORARY TABLE "post" ("id" int)' + }, + sqlite: { + text : 'CREATE TEMPORARY TABLE "post" ("id" int)', + string: 'CREATE TEMPORARY TABLE "post" ("id" int)' + }, + mysql: { + text : 'CREATE TEMPORARY TABLE `post` (`id` int)', + string: 'CREATE TEMPORARY TABLE `post` (`id` int)' + }, + //mssql: { + // text : 'CREATE TABLE [#post] ([id] int)', + // string: 'CREATE TABLE [#post] ([id] int)' + //}, + params: [] +}); + +var users = Table.define({ + name: 'users', + columns: { + id: { + primaryKey: true, + dataType: 'int', + references: { + table: "entity", + column: "id", + constraint: "DEFERRABLE INITIALLY DEFERRED" + } + } + } +}); + +Harness.test({ + query: users.create(), + pg: { + text: 'CREATE TABLE "users" ("id" int PRIMARY KEY REFERENCES "entity"("id") DEFERRABLE INITIALLY DEFERRED)', + string: 'CREATE TABLE "users" ("id" int PRIMARY KEY REFERENCES "entity"("id") DEFERRABLE INITIALLY DEFERRED)' + }, + sqlite: { + text: 'CREATE TABLE "users" ("id" int PRIMARY KEY REFERENCES "entity"("id") DEFERRABLE INITIALLY DEFERRED)', + string: 'CREATE TABLE "users" ("id" int PRIMARY KEY REFERENCES "entity"("id") DEFERRABLE INITIALLY DEFERRED)' + }, + mysql: { + text: 'CREATE TABLE `users` (`id` int PRIMARY KEY REFERENCES `entity`(`id`) DEFERRABLE INITIALLY DEFERRED)', + string: 'CREATE TABLE `users` (`id` int PRIMARY KEY REFERENCES `entity`(`id`) DEFERRABLE INITIALLY DEFERRED)' + }, + mssql: { + text: 'CREATE TABLE [users] ([id] int PRIMARY KEY REFERENCES [entity]([id]) DEFERRABLE INITIALLY DEFERRED)', + string: 'CREATE TABLE [users] ([id] int PRIMARY KEY REFERENCES [entity]([id]) DEFERRABLE INITIALLY DEFERRED)' + }, + oracle: { + text: 'CREATE TABLE "users" ("id" int PRIMARY KEY REFERENCES "entity"("id") DEFERRABLE INITIALLY DEFERRED)', + string: 'CREATE TABLE "users" ("id" int PRIMARY KEY REFERENCES "entity"("id") DEFERRABLE INITIALLY DEFERRED)' + }, + params: [] +}); + +// UNIQUE COLUMN TESTS +Harness.test({ + query: Table.define({ + name: 'post', + columns: [{ + name: 'id', + dataType: 'int', + //primaryKey: true, + //notNull: true, + unique: true + }] + }).create(), + pg: { + text : 'CREATE TABLE "post" ("id" int UNIQUE)', + string: 'CREATE TABLE "post" ("id" int UNIQUE)' + }, + sqlite: { + text : 'CREATE TABLE "post" ("id" int UNIQUE)', + string: 'CREATE TABLE "post" ("id" int UNIQUE)' + }, + mysql: { + text : 'CREATE TABLE `post` (`id` int UNIQUE)', + string: 'CREATE TABLE `post` (`id` int UNIQUE)' + }, + mssql: { + text : 'CREATE TABLE [post] ([id] int UNIQUE)', + string: 'CREATE TABLE [post] ([id] int UNIQUE)' + }, + oracle: { + text : 'CREATE TABLE "post" ("id" int UNIQUE)', + string: 'CREATE TABLE "post" ("id" int UNIQUE)' + }, + params: [] +}); + + +var noUsers = Table.define({ + name: 'no_users', + columns: { + id: { + primaryKey: true, + dataType: 'int', + references: { + table: "entity", + column: "id", + constraint: "" + } + } + } +}); + +Harness.test({ + query: noUsers.create(), + pg: { + text : 'CREATE TABLE "no_users" ("id" int PRIMARY KEY REFERENCES "entity"("id"))', + string: 'CREATE TABLE "no_users" ("id" int PRIMARY KEY REFERENCES "entity"("id"))' + }, + sqlite: { + text : 'CREATE TABLE "no_users" ("id" int PRIMARY KEY REFERENCES "entity"("id"))', + string: 'CREATE TABLE "no_users" ("id" int PRIMARY KEY REFERENCES "entity"("id"))' + }, + mysql: { + text : 'CREATE TABLE `no_users` (`id` int PRIMARY KEY REFERENCES `entity`(`id`))', + string: 'CREATE TABLE `no_users` (`id` int PRIMARY KEY REFERENCES `entity`(`id`))' + }, + mssql: { + text : 'CREATE TABLE [no_users] ([id] int PRIMARY KEY REFERENCES [entity]([id]))', + string: 'CREATE TABLE [no_users] ([id] int PRIMARY KEY REFERENCES [entity]([id]))' + }, + oracle: { + text : 'CREATE TABLE "no_users" ("id" int PRIMARY KEY REFERENCES "entity"("id"))', + string: 'CREATE TABLE "no_users" ("id" int PRIMARY KEY REFERENCES "entity"("id"))' + }, + params: [] +}); + +Harness.test({ + query: Table.define({ + name: 'post', + columns: [{ + name: 'id', + dataType: 'int', + //primaryKey: true, + notNull: true, + unique: true + }] + }).create(), + pg: { + text : 'CREATE TABLE "post" ("id" int NOT NULL UNIQUE)', + string: 'CREATE TABLE "post" ("id" int NOT NULL UNIQUE)' + }, + sqlite: { + text : 'CREATE TABLE "post" ("id" int NOT NULL UNIQUE)', + string: 'CREATE TABLE "post" ("id" int NOT NULL UNIQUE)' + }, + mysql: { + text : 'CREATE TABLE `post` (`id` int NOT NULL UNIQUE)', + string: 'CREATE TABLE `post` (`id` int NOT NULL UNIQUE)' + }, + mssql: { + text : 'CREATE TABLE [post] ([id] int NOT NULL UNIQUE)', + string: 'CREATE TABLE [post] ([id] int NOT NULL UNIQUE)' + }, + oracle: { + text : 'CREATE TABLE "post" ("id" int NOT NULL UNIQUE)', + string: 'CREATE TABLE "post" ("id" int NOT NULL UNIQUE)' + }, + params: [] +}); + +Harness.test({ + query: Table.define({ + name: 'post', + columns: [{ + name: 'id', + dataType: 'int', + primaryKey: true, + //notNull: true, + unique: true + }] + }).create(), + pg: { + text : 'CREATE TABLE "post" ("id" int PRIMARY KEY)', + string: 'CREATE TABLE "post" ("id" int PRIMARY KEY)' + }, + sqlite: { + text : 'CREATE TABLE "post" ("id" int PRIMARY KEY)', + string: 'CREATE TABLE "post" ("id" int PRIMARY KEY)' + }, + mysql: { + text : 'CREATE TABLE `post` (`id` int PRIMARY KEY)', + string: 'CREATE TABLE `post` (`id` int PRIMARY KEY)' + }, + mssql: { + text : 'CREATE TABLE [post] ([id] int PRIMARY KEY)', + string: 'CREATE TABLE [post] ([id] int PRIMARY KEY)' + }, + oracle: { + text : 'CREATE TABLE "post" ("id" int PRIMARY KEY)', + string: 'CREATE TABLE "post" ("id" int PRIMARY KEY)' + }, + params: [] +}); + +Harness.test({ + query: Table.define({ + name: 'post', + columns: [{ + name: 'id', + dataType: 'int', + primaryKey: true + }, { + name: 'blog_id', + dataType: 'int' + }, { + name: 'user_id', + dataType: 'int' + }], + foreignKeys: { + table: 'users', + columns: [ 'blog_id', 'user_id' ], + refColumns: [ 'id', 'user_id' ] + } + }).create(), + pg: { + text : 'CREATE TABLE "post" ("id" int PRIMARY KEY, "blog_id" int, "user_id" int, FOREIGN KEY ( "blog_id", "user_id" ) REFERENCES "users" ( "id", "user_id" ))', + string: 'CREATE TABLE "post" ("id" int PRIMARY KEY, "blog_id" int, "user_id" int, FOREIGN KEY ( "blog_id", "user_id" ) REFERENCES "users" ( "id", "user_id" ))' + }, + sqlite: { + text : 'CREATE TABLE "post" ("id" int PRIMARY KEY, "blog_id" int, "user_id" int, FOREIGN KEY ( "blog_id", "user_id" ) REFERENCES "users" ( "id", "user_id" ))', + string: 'CREATE TABLE "post" ("id" int PRIMARY KEY, "blog_id" int, "user_id" int, FOREIGN KEY ( "blog_id", "user_id" ) REFERENCES "users" ( "id", "user_id" ))' + }, + mysql: { + text : 'CREATE TABLE `post` (`id` int PRIMARY KEY, `blog_id` int, `user_id` int, FOREIGN KEY ( `blog_id`, `user_id` ) REFERENCES `users` ( `id`, `user_id` ))', + string: 'CREATE TABLE `post` (`id` int PRIMARY KEY, `blog_id` int, `user_id` int, FOREIGN KEY ( `blog_id`, `user_id` ) REFERENCES `users` ( `id`, `user_id` ))' + }, + params: [] +}); + +Harness.test({ + query: Table.define({ + name: 'replies', + columns: [{ + name: 'id', + dataType: 'int', + primaryKey: true + }, { + name: 'blog_id', + dataType: 'int' + }, { + name: 'post_id', + dataType: 'int' + }, { + name: 'user_id', + dataType: 'int' + }], + foreignKeys: [{ + table: 'users', + columns: [ 'blog_id', 'user_id' ], + onDelete: 'no action' + }, { + name: 'posts_idx', + table: 'posts', + columns: [ 'blog_id', 'post_id' ], + refColumns: [ 'blog_id', 'id' ], + onDelete: 'cascade', + onUpdate: 'set default' + }] + }).create(), + pg: { + text : 'CREATE TABLE "replies" ("id" int PRIMARY KEY, "blog_id" int, "post_id" int, "user_id" int, FOREIGN KEY ( "blog_id", "user_id" ) REFERENCES "users" ON DELETE NO ACTION, CONSTRAINT "posts_idx" FOREIGN KEY ( "blog_id", "post_id" ) REFERENCES "posts" ( "blog_id", "id" ) ON DELETE CASCADE ON UPDATE SET DEFAULT)', + string: 'CREATE TABLE "replies" ("id" int PRIMARY KEY, "blog_id" int, "post_id" int, "user_id" int, FOREIGN KEY ( "blog_id", "user_id" ) REFERENCES "users" ON DELETE NO ACTION, CONSTRAINT "posts_idx" FOREIGN KEY ( "blog_id", "post_id" ) REFERENCES "posts" ( "blog_id", "id" ) ON DELETE CASCADE ON UPDATE SET DEFAULT)' + }, + sqlite: { + text : 'CREATE TABLE "replies" ("id" int PRIMARY KEY, "blog_id" int, "post_id" int, "user_id" int, FOREIGN KEY ( "blog_id", "user_id" ) REFERENCES "users" ON DELETE NO ACTION, CONSTRAINT "posts_idx" FOREIGN KEY ( "blog_id", "post_id" ) REFERENCES "posts" ( "blog_id", "id" ) ON DELETE CASCADE ON UPDATE SET DEFAULT)', + string: 'CREATE TABLE "replies" ("id" int PRIMARY KEY, "blog_id" int, "post_id" int, "user_id" int, FOREIGN KEY ( "blog_id", "user_id" ) REFERENCES "users" ON DELETE NO ACTION, CONSTRAINT "posts_idx" FOREIGN KEY ( "blog_id", "post_id" ) REFERENCES "posts" ( "blog_id", "id" ) ON DELETE CASCADE ON UPDATE SET DEFAULT)' + }, + mysql: { + text : 'CREATE TABLE `replies` (`id` int PRIMARY KEY, `blog_id` int, `post_id` int, `user_id` int, FOREIGN KEY ( `blog_id`, `user_id` ) REFERENCES `users` ON DELETE NO ACTION, CONSTRAINT `posts_idx` FOREIGN KEY ( `blog_id`, `post_id` ) REFERENCES `posts` ( `blog_id`, `id` ) ON DELETE CASCADE ON UPDATE SET DEFAULT)', + string: 'CREATE TABLE `replies` (`id` int PRIMARY KEY, `blog_id` int, `post_id` int, `user_id` int, FOREIGN KEY ( `blog_id`, `user_id` ) REFERENCES `users` ON DELETE NO ACTION, CONSTRAINT `posts_idx` FOREIGN KEY ( `blog_id`, `post_id` ) REFERENCES `posts` ( `blog_id`, `id` ) ON DELETE CASCADE ON UPDATE SET DEFAULT)' + }, + params: [] +}); diff --git a/test/dialects/create-view-tests.js b/test/dialects/create-view-tests.js new file mode 100644 index 00000000..977f540a --- /dev/null +++ b/test/dialects/create-view-tests.js @@ -0,0 +1,80 @@ +'use strict'; + +var Harness = require('./support'); +var user = Harness.defineUserTable(); + +//simple view create +Harness.test({ + query: user.select(user.star()).createView('allUsersView'), + pg: { + text : '(CREATE VIEW "allUsersView" AS SELECT "user".* FROM "user")', + string: '(CREATE VIEW "allUsersView" AS SELECT "user".* FROM "user")' + }, + sqlite: { + text : '(CREATE VIEW "allUsersView" AS SELECT "user".* FROM "user")', + string: '(CREATE VIEW "allUsersView" AS SELECT "user".* FROM "user")' + }, + mysql: { + text : '(CREATE VIEW `allUsersView` AS SELECT `user`.* FROM `user`)', + string: '(CREATE VIEW `allUsersView` AS SELECT `user`.* FROM `user`)' + }, + mssql: { + text : '(CREATE VIEW [allUsersView] AS SELECT [user].* FROM [user])', + string: '(CREATE VIEW [allUsersView] AS SELECT [user].* FROM [user])' + }, + oracle: { + text : '(CREATE VIEW "allUsersView" AS SELECT "user".* FROM "user")', + string: '(CREATE VIEW "allUsersView" AS SELECT "user".* FROM "user")' + } +}); + +//create view with parameters +Harness.test({ + query: user.select(user.star()).where(user.id.equals(1)).createView('oneUserView'), + pg: { + text : '(CREATE VIEW "oneUserView" AS SELECT "user".* FROM "user" WHERE ("user"."id" = 1))', + string: '(CREATE VIEW "oneUserView" AS SELECT "user".* FROM "user" WHERE ("user"."id" = 1))' + }, + sqlite: { + text : '(CREATE VIEW "oneUserView" AS SELECT "user".* FROM "user" WHERE ("user"."id" = 1))', + string: '(CREATE VIEW "oneUserView" AS SELECT "user".* FROM "user" WHERE ("user"."id" = 1))' + }, + mysql: { + text : '(CREATE VIEW `oneUserView` AS SELECT `user`.* FROM `user` WHERE (`user`.`id` = 1))', + string: '(CREATE VIEW `oneUserView` AS SELECT `user`.* FROM `user` WHERE (`user`.`id` = 1))' + }, + mssql: { + text : '(CREATE VIEW [oneUserView] AS SELECT [user].* FROM [user] WHERE ([user].[id] = 1))', + string: '(CREATE VIEW [oneUserView] AS SELECT [user].* FROM [user] WHERE ([user].[id] = 1))' + }, + oracle: { + text : '(CREATE VIEW "oneUserView" AS SELECT "user".* FROM "user" WHERE ("user"."id" = 1))', + string: '(CREATE VIEW "oneUserView" AS SELECT "user".* FROM "user" WHERE ("user"."id" = 1))' + } +}); + +//Tests error raised for non-SELECT create view attempts +Harness.test({ + query: user.delete().where(user.id.equals(1)).createView('oneUserView'), + pg: { + text : 'Create View requires a Select.', + throws: true + }, + sqlite: { + text : 'Create View requires a Select.', + throws: true + }, + mysql: { + text : 'Create View requires a Select.', + throws: true + }, + mssql: { + text : 'Create View requires a Select.', + throws: true + }, + oracle: { + text : 'Create View requires a Select.', + throws: true + }, + params: [] +}); diff --git a/test/dialects/date-tests.js b/test/dialects/date-tests.js new file mode 100644 index 00000000..dd2c4d64 --- /dev/null +++ b/test/dialects/date-tests.js @@ -0,0 +1,228 @@ +'use strict'; + +var Harness = require('./support'); +var customer = Harness.defineCustomerTable(); +var Sql = require('../../lib'); + +Harness.test({ + query: customer.select(Sql.functions.YEAR(customer.metadata)), + pg: { + text : 'SELECT EXTRACT(YEAR FROM "customer"."metadata") FROM "customer"', + string: 'SELECT EXTRACT(YEAR FROM "customer"."metadata") FROM "customer"' + }, + sqlite: { + text : 'SELECT strftime(\'%Y\', "customer"."metadata") FROM "customer"', + string: 'SELECT strftime(\'%Y\', "customer"."metadata") FROM "customer"' + }, + mysql: { + text : 'SELECT YEAR(`customer`.`metadata`) FROM `customer`', + string: 'SELECT YEAR(`customer`.`metadata`) FROM `customer`' + }, + mssql: { + text : 'SELECT DATEPART(year, [customer].[metadata]) FROM [customer]', + string: 'SELECT DATEPART(year, [customer].[metadata]) FROM [customer]' + }, + oracle: { + text : 'SELECT EXTRACT(YEAR FROM "customer"."metadata") FROM "customer"', + string: 'SELECT EXTRACT(YEAR FROM "customer"."metadata") FROM "customer"' + }, + params: [] +}); + +Harness.test({ + query: customer.select(Sql.functions.MONTH(customer.metadata)), + pg: { + text : 'SELECT EXTRACT(MONTH FROM "customer"."metadata") FROM "customer"', + string: 'SELECT EXTRACT(MONTH FROM "customer"."metadata") FROM "customer"' + }, + sqlite: { + text: 'SELECT strftime(\'%m\', datetime("customer"."metadata"/1000, "unixepoch")) FROM "customer"', + string: 'SELECT strftime(\'%m\', datetime("customer"."metadata"/1000, "unixepoch")) FROM "customer"', + config: { + dateTimeMillis: true + } + }, + mysql: { + text : 'SELECT MONTH(`customer`.`metadata`) FROM `customer`', + string: 'SELECT MONTH(`customer`.`metadata`) FROM `customer`' + }, + mssql: { + text : 'SELECT DATEPART(month, [customer].[metadata]) FROM [customer]', + string: 'SELECT DATEPART(month, [customer].[metadata]) FROM [customer]' + }, + oracle: { + text : 'SELECT EXTRACT(MONTH FROM "customer"."metadata") FROM "customer"', + string: 'SELECT EXTRACT(MONTH FROM "customer"."metadata") FROM "customer"' + }, + params: [] +}); + +Harness.test({ + query: customer.select(Sql.functions.DAY(customer.metadata)), + pg: { + text : 'SELECT EXTRACT(DAY FROM "customer"."metadata") FROM "customer"', + string: 'SELECT EXTRACT(DAY FROM "customer"."metadata") FROM "customer"' + }, + sqlite: { + text : 'SELECT strftime(\'%d\', "customer"."metadata") FROM "customer"', + string: 'SELECT strftime(\'%d\', "customer"."metadata") FROM "customer"' + }, + mysql: { + text : 'SELECT DAY(`customer`.`metadata`) FROM `customer`', + string: 'SELECT DAY(`customer`.`metadata`) FROM `customer`' + }, + mssql: { + text : 'SELECT DATEPART(day, [customer].[metadata]) FROM [customer]', + string: 'SELECT DATEPART(day, [customer].[metadata]) FROM [customer]' + }, + oracle: { + text : 'SELECT EXTRACT(DAY FROM "customer"."metadata") FROM "customer"', + string: 'SELECT EXTRACT(DAY FROM "customer"."metadata") FROM "customer"' + }, + params: [] +}); + +Harness.test({ + query: customer.select(Sql.functions.HOUR(customer.metadata)), + pg: { + text : 'SELECT EXTRACT(HOUR FROM "customer"."metadata") FROM "customer"', + string: 'SELECT EXTRACT(HOUR FROM "customer"."metadata") FROM "customer"' + }, + sqlite: { + text: 'SELECT strftime(\'%H\', datetime("customer"."metadata"/1000, "unixepoch")) FROM "customer"', + string: 'SELECT strftime(\'%H\', datetime("customer"."metadata"/1000, "unixepoch")) FROM "customer"', + config: { + dateTimeMillis: true + } + }, + mysql: { + text : 'SELECT HOUR(`customer`.`metadata`) FROM `customer`', + string: 'SELECT HOUR(`customer`.`metadata`) FROM `customer`' + }, + mssql: { + text : 'SELECT DATEPART(hour, [customer].[metadata]) FROM [customer]', + string: 'SELECT DATEPART(hour, [customer].[metadata]) FROM [customer]' + }, + oracle: { + text : 'SELECT EXTRACT(HOUR FROM "customer"."metadata") FROM "customer"', + string: 'SELECT EXTRACT(HOUR FROM "customer"."metadata") FROM "customer"' + }, + params: [] +}); + +Harness.test({ + query: customer.select(Sql.functions.CURRENT_TIMESTAMP()), + pg: { + text : 'SELECT CURRENT_TIMESTAMP FROM "customer"', + string: 'SELECT CURRENT_TIMESTAMP FROM "customer"' + }, + sqlite: { + text : 'SELECT CURRENT_TIMESTAMP FROM "customer"', + string: 'SELECT CURRENT_TIMESTAMP FROM "customer"' + }, + mysql: { + text : 'SELECT CURRENT_TIMESTAMP FROM `customer`', + string: 'SELECT CURRENT_TIMESTAMP FROM `customer`' + }, + mssql: { + text : 'SELECT CURRENT_TIMESTAMP FROM [customer]', + string: 'SELECT CURRENT_TIMESTAMP FROM [customer]' + }, + oracle: { + text : 'SELECT CURRENT_TIMESTAMP FROM "customer"', + string: 'SELECT CURRENT_TIMESTAMP FROM "customer"' + }, + params: [] +}); + +Harness.test({ + query: Sql.select(Sql.functions.CURRENT_TIMESTAMP().plus(Sql.interval({hours:1}))), + pg: { + text : 'SELECT (CURRENT_TIMESTAMP + INTERVAL \'1 HOUR\')', + string: 'SELECT (CURRENT_TIMESTAMP + INTERVAL \'1 HOUR\')' + }, + mysql: { + text : 'SELECT (CURRENT_TIMESTAMP + INTERVAL \'1:0:0\' HOUR_SECOND)', + string: 'SELECT (CURRENT_TIMESTAMP + INTERVAL \'1:0:0\' HOUR_SECOND)' + }, + params: [] +}); + +Harness.test({ + query: Sql.select(Sql.functions.CURRENT_TIMESTAMP().minus(Sql.interval({years:3}))), + pg: { + text : 'SELECT (CURRENT_TIMESTAMP - INTERVAL \'3 YEAR\')', + string: 'SELECT (CURRENT_TIMESTAMP - INTERVAL \'3 YEAR\')' + }, + mysql: { + text : 'SELECT (CURRENT_TIMESTAMP - INTERVAL 3 YEAR)', + string: 'SELECT (CURRENT_TIMESTAMP - INTERVAL 3 YEAR)' + }, + params: [] +}); + +Harness.test({ + query: Sql.select(Sql.functions.CURRENT_TIMESTAMP().minus(Sql.interval({years:3, months:2}))), + pg: { + text : 'SELECT (CURRENT_TIMESTAMP - INTERVAL \'3 YEAR 2 MONTH\')', + string: 'SELECT (CURRENT_TIMESTAMP - INTERVAL \'3 YEAR 2 MONTH\')' + }, + mysql: { + text : 'SELECT (CURRENT_TIMESTAMP - INTERVAL \'3-2\' YEAR_MONTH)', + string: 'SELECT (CURRENT_TIMESTAMP - INTERVAL \'3-2\' YEAR_MONTH)' + }, + params: [] +}); + +Harness.test({ + query: Sql.select(Sql.functions.CURRENT_TIMESTAMP().plus(Sql.interval({hours:1, minutes:20}))), + pg: { + text : 'SELECT (CURRENT_TIMESTAMP + INTERVAL \'1 HOUR 20 MINUTE\')', + string: 'SELECT (CURRENT_TIMESTAMP + INTERVAL \'1 HOUR 20 MINUTE\')' + }, + mysql: { + text : 'SELECT (CURRENT_TIMESTAMP + INTERVAL \'1:20:0\' HOUR_SECOND)', + string: 'SELECT (CURRENT_TIMESTAMP + INTERVAL \'1:20:0\' HOUR_SECOND)' + }, + params: [] +}); + +Harness.test({ + query: Sql.select(Sql.functions.CURRENT_TIMESTAMP().plus(Sql.interval({hours:'sql\'injection', minutes:20}))), + pg: { + text : 'SELECT (CURRENT_TIMESTAMP + INTERVAL \'20 MINUTE\')', + string: 'SELECT (CURRENT_TIMESTAMP + INTERVAL \'20 MINUTE\')' + }, + mysql: { + text : 'SELECT (CURRENT_TIMESTAMP + INTERVAL \'0:20:0\' HOUR_SECOND)', + string: 'SELECT (CURRENT_TIMESTAMP + INTERVAL \'0:20:0\' HOUR_SECOND)' + }, + params: [] +}); + +Harness.test({ + query: Sql.select(Sql.functions.CURRENT_TIMESTAMP().minus(Sql.interval({days: 1, hours:5, minutes: 'sql\'injection'}))), + pg: { + text : 'SELECT (CURRENT_TIMESTAMP - INTERVAL \'1 DAY 5 HOUR\')', + string: 'SELECT (CURRENT_TIMESTAMP - INTERVAL \'1 DAY 5 HOUR\')' + }, + mysql: { + text : 'SELECT (CURRENT_TIMESTAMP - INTERVAL \'1 5:0:0\' DAY_SECOND)', + string: 'SELECT (CURRENT_TIMESTAMP - INTERVAL \'1 5:0:0\' DAY_SECOND)' + }, + params: [] +}); + +Harness.test({ + query: Sql.select(Sql.functions.CURRENT_TIMESTAMP().minus(Sql.interval({years: 2, months: 5}))), + pg: { + text : 'SELECT (CURRENT_TIMESTAMP - INTERVAL \'2 YEAR 5 MONTH\')', + string: 'SELECT (CURRENT_TIMESTAMP - INTERVAL \'2 YEAR 5 MONTH\')' + }, + mysql: { + text : 'SELECT (CURRENT_TIMESTAMP - INTERVAL \'2-5\' YEAR_MONTH)', + string: 'SELECT (CURRENT_TIMESTAMP - INTERVAL \'2-5\' YEAR_MONTH)' + }, + params: [] +}); + diff --git a/test/dialects/delete-tests.js b/test/dialects/delete-tests.js index 71c27d95..51fe3026 100644 --- a/test/dialects/delete-tests.js +++ b/test/dialects/delete-tests.js @@ -2,6 +2,7 @@ var Harness = require('./support'); var post = Harness.definePostTable(); +var user = Harness.defineUserTable(); Harness.test({ query: post.delete().where(post.content.equals("hello's world")), @@ -17,9 +18,75 @@ Harness.test({ text : 'DELETE FROM `post` WHERE (`post`.`content` = ?)', string: 'DELETE FROM `post` WHERE (`post`.`content` = \'hello\'\'s world\')' }, + mssql: { + text : 'DELETE FROM [post] WHERE ([post].[content] = @1)', + string: "DELETE FROM [post] WHERE ([post].[content] = 'hello''s world')" + }, + oracle: { + text : 'DELETE FROM "post" WHERE ("post"."content" = :1)', + string: 'DELETE FROM "post" WHERE ("post"."content" = \'hello\'\'s world\')' + }, params: ["hello's world"] }); +Harness.test({ + query: post.delete(post).from(post), + pg: { + text: 'DELETE "post" FROM "post"', + string: 'DELETE "post" FROM "post"' + }, + sqlite: { + text: 'DELETE "post" FROM "post"', + string: 'DELETE "post" FROM "post"' + }, + mysql: { + text: 'DELETE `post` FROM `post`', + string: 'DELETE `post` FROM `post`' + }, + params: [] +}); + +Harness.test({ + query: post.delete([post, post]).from(post), + pg: { + text: 'DELETE "post", "post" FROM "post"', + string: 'DELETE "post", "post" FROM "post"' + }, + sqlite: { + text: 'DELETE "post", "post" FROM "post"', + string: 'DELETE "post", "post" FROM "post"' + }, + mysql: { + text: 'DELETE `post`, `post` FROM `post`', + string: 'DELETE `post`, `post` FROM `post`' + }, + params: [] +}); + +Harness.test({ + query: user + .delete(user) + .from(user.join(post).on(post.userId.equals(user.id))) + .where(post.content.equals('foo')), + pg: { + text: 'DELETE "user" FROM "user" INNER JOIN "post" ON ("post"."userId" = "user"."id") WHERE ("post"."content" = $1)', + string: 'DELETE "user" FROM "user" INNER JOIN "post" ON ("post"."userId" = "user"."id") WHERE ("post"."content" = \'foo\')' + }, + sqlite: { + text: 'DELETE "user" FROM "user" INNER JOIN "post" ON ("post"."userId" = "user"."id") WHERE ("post"."content" = $1)', + string: 'DELETE "user" FROM "user" INNER JOIN "post" ON ("post"."userId" = "user"."id") WHERE ("post"."content" = \'foo\')' + }, + mysql: { + text: 'DELETE `user` FROM `user` INNER JOIN `post` ON (`post`.`userId` = `user`.`id`) WHERE (`post`.`content` = ?)', + string: 'DELETE `user` FROM `user` INNER JOIN `post` ON (`post`.`userId` = `user`.`id`) WHERE (`post`.`content` = \'foo\')' + }, + oracle: { + text: 'DELETE "user" FROM "user" INNER JOIN "post" ON ("post"."userId" = "user"."id") WHERE ("post"."content" = :1)', + string: 'DELETE "user" FROM "user" INNER JOIN "post" ON ("post"."userId" = "user"."id") WHERE ("post"."content" = \'foo\')' + }, + params: [ 'foo' ] +}); + Harness.test({ query: post.delete().where({ content: '' @@ -36,6 +103,14 @@ Harness.test({ text : 'DELETE FROM `post` WHERE (`post`.`content` = ?)', string: 'DELETE FROM `post` WHERE (`post`.`content` = \'\')' }, + mssql: { + text : 'DELETE FROM [post] WHERE ([post].[content] = @1)', + string: "DELETE FROM [post] WHERE ([post].[content] = '')" + }, + oracle: { + text : 'DELETE FROM "post" WHERE ("post"."content" = :1)', + string: 'DELETE FROM "post" WHERE ("post"."content" = \'\')' + }, params: [''] }); @@ -55,6 +130,14 @@ Harness.test({ text : 'DELETE FROM `post` WHERE (`post`.`content` = ?)', string: 'DELETE FROM `post` WHERE (`post`.`content` = \'\')' }, + mssql: { + text : 'DELETE FROM [post] WHERE ([post].[content] = @1)', + string: "DELETE FROM [post] WHERE ([post].[content] = '')" + }, + oracle: { + text : 'DELETE FROM "post" WHERE ("post"."content" = :1)', + string: 'DELETE FROM "post" WHERE ("post"."content" = \'\')' + }, params: [''] }); @@ -74,5 +157,13 @@ Harness.test({ text : 'DELETE FROM `post` WHERE ((`post`.`content` = ?) OR (`post`.`content` IS NULL))', string: 'DELETE FROM `post` WHERE ((`post`.`content` = \'\') OR (`post`.`content` IS NULL))' }, + mssql: { + text : 'DELETE FROM [post] WHERE (([post].[content] = @1) OR ([post].[content] IS NULL))', + string: "DELETE FROM [post] WHERE (([post].[content] = '') OR ([post].[content] IS NULL))" + }, + oracle: { + text : 'DELETE FROM "post" WHERE (("post"."content" = :1) OR ("post"."content" IS NULL))', + string: 'DELETE FROM "post" WHERE (("post"."content" = \'\') OR ("post"."content" IS NULL))' + }, params: [''] }); diff --git a/test/dialects/distinct-on-tests.js b/test/dialects/distinct-on-tests.js new file mode 100644 index 00000000..d238f928 --- /dev/null +++ b/test/dialects/distinct-on-tests.js @@ -0,0 +1,24 @@ +'use strict'; + +var Harness = require('./support'); +var user = Harness.defineUserTable(); +var Sql = require('../../lib').setDialect('postgres'); + +Harness.test({ + query: user.select().distinctOn(user.id), + pg: { + text : 'SELECT DISTINCT ON("user"."id") "user".* FROM "user"', + string: 'SELECT DISTINCT ON("user"."id") "user".* FROM "user"' + }, + params: [] +}); + +Harness.test({ + query: user.select(user.id,user.name).distinctOn(user.id), + pg: { + text : 'SELECT DISTINCT ON("user"."id") "user"."id", "user"."name" FROM "user"', + string: 'SELECT DISTINCT ON("user"."id") "user"."id", "user"."name" FROM "user"' + }, + params: [] +}); + diff --git a/test/dialects/distinct-tests.js b/test/dialects/distinct-tests.js index 5a767183..9f922bff 100644 --- a/test/dialects/distinct-tests.js +++ b/test/dialects/distinct-tests.js @@ -17,6 +17,14 @@ Harness.test({ text : 'SELECT DISTINCT(`user`.`id`) FROM `user`', string: 'SELECT DISTINCT(`user`.`id`) FROM `user`' }, + mssql: { + text : 'SELECT DISTINCT([user].[id]) FROM [user]', + string: 'SELECT DISTINCT([user].[id]) FROM [user]' + }, + oracle: { + text : 'SELECT DISTINCT("user"."id") FROM "user"', + string: 'SELECT DISTINCT("user"."id") FROM "user"' + }, params: [] }); @@ -34,5 +42,91 @@ Harness.test({ text : 'SELECT COUNT(DISTINCT(`user`.`id`)) AS `count` FROM `user`', string: 'SELECT COUNT(DISTINCT(`user`.`id`)) AS `count` FROM `user`' }, + mssql: { + text : 'SELECT COUNT(DISTINCT([user].[id])) AS [count] FROM [user]', + string: 'SELECT COUNT(DISTINCT([user].[id])) AS [count] FROM [user]' + }, + oracle: { + text : 'SELECT COUNT(DISTINCT("user"."id")) "count" FROM "user"', + string: 'SELECT COUNT(DISTINCT("user"."id")) "count" FROM "user"' + }, + params: [] +}); + +// BELOW HERE TEST DISTINCT ON THE ENTIRE RESULTS SET, NOT JUST ONE COLUMN + +Harness.test({ + query: user.select().distinct(), + pg: { + text : 'SELECT DISTINCT "user".* FROM "user"', + string: 'SELECT DISTINCT "user".* FROM "user"' + }, + sqlite: { + text : 'SELECT DISTINCT "user".* FROM "user"', + string: 'SELECT DISTINCT "user".* FROM "user"' + }, + mysql: { + text : 'SELECT DISTINCT `user`.* FROM `user`', + string: 'SELECT DISTINCT `user`.* FROM `user`' + }, + mssql: { + text : 'SELECT DISTINCT [user].* FROM [user]', + string: 'SELECT DISTINCT [user].* FROM [user]' + }, + oracle: { + text : 'SELECT DISTINCT "user".* FROM "user"', + string: 'SELECT DISTINCT "user".* FROM "user"' + }, + params: [] +}); + +Harness.test({ + query: user.select(user.id).distinct(), + pg: { + text : 'SELECT DISTINCT "user"."id" FROM "user"', + string: 'SELECT DISTINCT "user"."id" FROM "user"' + }, + sqlite: { + text : 'SELECT DISTINCT "user"."id" FROM "user"', + string: 'SELECT DISTINCT "user"."id" FROM "user"' + }, + mysql: { + text : 'SELECT DISTINCT `user`.`id` FROM `user`', + string: 'SELECT DISTINCT `user`.`id` FROM `user`' + }, + mssql: { + text : 'SELECT DISTINCT [user].[id] FROM [user]', + string: 'SELECT DISTINCT [user].[id] FROM [user]' + }, + oracle: { + text : 'SELECT DISTINCT "user"."id" FROM "user"', + string: 'SELECT DISTINCT "user"."id" FROM "user"' + }, + params: [] +}); + +Harness.test({ + query: user.select(user.id,user.name).distinct(), + pg: { + text : 'SELECT DISTINCT "user"."id", "user"."name" FROM "user"', + string: 'SELECT DISTINCT "user"."id", "user"."name" FROM "user"' + }, + sqlite: { + text : 'SELECT DISTINCT "user"."id", "user"."name" FROM "user"', + string: 'SELECT DISTINCT "user"."id", "user"."name" FROM "user"' + }, + mysql: { + text : 'SELECT DISTINCT `user`.`id`, `user`.`name` FROM `user`', + string: 'SELECT DISTINCT `user`.`id`, `user`.`name` FROM `user`' + }, + mssql: { + text : 'SELECT DISTINCT [user].[id], [user].[name] FROM [user]', + string: 'SELECT DISTINCT [user].[id], [user].[name] FROM [user]' + }, + oracle: { + text : 'SELECT DISTINCT "user"."id", "user"."name" FROM "user"', + string: 'SELECT DISTINCT "user"."id", "user"."name" FROM "user"' + }, params: [] }); + diff --git a/test/dialects/drop-table-tests.js b/test/dialects/drop-table-tests.js index 6e8943b5..eb183e64 100644 --- a/test/dialects/drop-table-tests.js +++ b/test/dialects/drop-table-tests.js @@ -17,6 +17,14 @@ Harness.test({ text : 'DROP TABLE `post`', string: 'DROP TABLE `post`' }, + mssql: { + text : 'DROP TABLE [post]', + string: 'DROP TABLE [post]' + }, + oracle: { + text : 'DROP TABLE "post"', + string: 'DROP TABLE "post"' + }, params: [] }); @@ -34,5 +42,55 @@ Harness.test({ text : 'DROP TABLE IF EXISTS `post`', string: 'DROP TABLE IF EXISTS `post`' }, + mssql: { + text : 'IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = [post]) BEGIN DROP TABLE [post] END', + string: 'IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = [post]) BEGIN DROP TABLE [post] END' + }, + oracle: { + text : 'BEGIN EXECUTE IMMEDIATE \'DROP TABLE "post"\'; EXCEPTION WHEN OTHERS THEN IF SQLCODE != -942 THEN RAISE; END IF; END;', + string: 'BEGIN EXECUTE IMMEDIATE \'DROP TABLE "post"\'; EXCEPTION WHEN OTHERS THEN IF SQLCODE != -942 THEN RAISE; END IF; END;' + }, + params: [] +}); + +Harness.test({ + query: post.drop().cascade(), + pg: { + text : 'DROP TABLE "post" CASCADE', + string: 'DROP TABLE "post" CASCADE' + }, + sqlite: { + text : 'Sqlite do not support CASCADE in DROP TABLE', + throws: true + }, + mysql: { + text : 'DROP TABLE `post` CASCADE', + string: 'DROP TABLE `post` CASCADE' + }, + oracle: { + text : 'DROP TABLE "post" CASCADE CONSTRAINTS', + string: 'DROP TABLE "post" CASCADE CONSTRAINTS' + }, + params: [] +}); + +Harness.test({ + query: post.drop().restrict(), + pg: { + text : 'DROP TABLE "post" RESTRICT', + string: 'DROP TABLE "post" RESTRICT' + }, + sqlite: { + text : 'Sqlite do not support RESTRICT in DROP TABLE', + throws: true + }, + mysql: { + text : 'DROP TABLE `post` RESTRICT', + string: 'DROP TABLE `post` RESTRICT' + }, + oracle: { + text : 'Oracle do not support RESTRICT in DROP TABLE', + throws: true + }, params: [] }); diff --git a/test/dialects/for-share-tests.js b/test/dialects/for-share-tests.js new file mode 100644 index 00000000..4b9627fa --- /dev/null +++ b/test/dialects/for-share-tests.js @@ -0,0 +1,23 @@ +'use strict'; + +var Harness = require('./support'); +var post = Harness.definePostTable(); +var user = Harness.defineUserTable(); + +Harness.test({ + query: post.select(post.star()).forShare(), + pg: { + text : 'SELECT "post".* FROM "post" FOR SHARE', + string: 'SELECT "post".* FROM "post" FOR SHARE' + }, + params: [] +}); + +Harness.test({ + query: post.select(post.star()).from(post.join(user).on(user.id.equals(post.userId))).where(post.content.equals('foo')).forShare(), + pg: { + text : 'SELECT "post".* FROM "post" INNER JOIN "user" ON ("user"."id" = "post"."userId") WHERE ("post"."content" = $1) FOR SHARE', + string: 'SELECT "post".* FROM "post" INNER JOIN "user" ON ("user"."id" = "post"."userId") WHERE ("post"."content" = \'foo\') FOR SHARE' + }, + params: ["foo"] +}); diff --git a/test/dialects/for-update-tests.js b/test/dialects/for-update-tests.js new file mode 100644 index 00000000..19a01e20 --- /dev/null +++ b/test/dialects/for-update-tests.js @@ -0,0 +1,31 @@ +'use strict'; + +var Harness = require('./support'); +var post = Harness.definePostTable(); +var user = Harness.defineUserTable(); + +Harness.test({ + query: post.select(post.star()).forUpdate(), + pg: { + text : 'SELECT "post".* FROM "post" FOR UPDATE', + string: 'SELECT "post".* FROM "post" FOR UPDATE' + }, + mysql: { + text : 'SELECT `post`.* FROM `post` FOR UPDATE', + string: 'SELECT `post`.* FROM `post` FOR UPDATE' + }, + params: [] +}); + +Harness.test({ + query: post.select(post.star()).from(post.join(user).on(user.id.equals(post.userId))).where(post.content.equals('foo')).forUpdate(), + pg: { + text : 'SELECT "post".* FROM "post" INNER JOIN "user" ON ("user"."id" = "post"."userId") WHERE ("post"."content" = $1) FOR UPDATE', + string: 'SELECT "post".* FROM "post" INNER JOIN "user" ON ("user"."id" = "post"."userId") WHERE ("post"."content" = \'foo\') FOR UPDATE' + }, + mysql: { + text : 'SELECT `post`.* FROM `post` INNER JOIN `user` ON (`user`.`id` = `post`.`userId`) WHERE (`post`.`content` = ?) FOR UPDATE', + string: 'SELECT `post`.* FROM `post` INNER JOIN `user` ON (`user`.`id` = `post`.`userId`) WHERE (`post`.`content` = \'foo\') FOR UPDATE' + }, + params: ["foo"] +}); diff --git a/test/dialects/from-clause-tests.js b/test/dialects/from-clause-tests.js index d0d8fc01..673087db 100644 --- a/test/dialects/from-clause-tests.js +++ b/test/dialects/from-clause-tests.js @@ -17,6 +17,14 @@ Harness.test({ mysql: { text : 'SELECT `user`.* FROM `user` , `post`', string: 'SELECT `user`.* FROM `user` , `post`' + }, + mssql: { + text : 'SELECT [user].* FROM [user] , [post]', + string: 'SELECT [user].* FROM [user] , [post]' + }, + oracle: { + text : 'SELECT "user".* FROM "user" , "post"', + string: 'SELECT "user".* FROM "user" , "post"' } }); @@ -33,5 +41,53 @@ Harness.test({ mysql: { text : 'SELECT `user`.*, `post`.* FROM `user` , `post`', string: 'SELECT `user`.*, `post`.* FROM `user` , `post`' + }, + mssql: { + text : 'SELECT [user].*, [post].* FROM [user] , [post]', + string: 'SELECT [user].*, [post].* FROM [user] , [post]' + }, + oracle: { + text : 'SELECT "user".*, "post".* FROM "user" , "post"', + string: 'SELECT "user".*, "post".* FROM "user" , "post"' } }); + +Harness.test({ + query: user.select(user.star()).from(user, post), + pg: { + text : 'SELECT "user".* FROM "user" , "post"', + string: 'SELECT "user".* FROM "user" , "post"' + }, + sqlite: { + text : 'SELECT "user".* FROM "user" , "post"', + string: 'SELECT "user".* FROM "user" , "post"' + }, + mysql: { + text : 'SELECT `user`.* FROM `user` , `post`', + string: 'SELECT `user`.* FROM `user` , `post`' + }, + oracle: { + text : 'SELECT "user".* FROM "user" , "post"', + string: 'SELECT "user".* FROM "user" , "post"' + } +}); + +Harness.test({ + query: user.select(user.star()).from([user, post]), + pg: { + text : 'SELECT "user".* FROM "user" , "post"', + string: 'SELECT "user".* FROM "user" , "post"' + }, + sqlite: { + text : 'SELECT "user".* FROM "user" , "post"', + string: 'SELECT "user".* FROM "user" , "post"' + }, + mysql: { + text : 'SELECT `user`.* FROM `user` , `post`', + string: 'SELECT `user`.* FROM `user` , `post`' + }, + oracle: { + text : 'SELECT "user".* FROM "user" , "post"', + string: 'SELECT "user".* FROM "user" , "post"' + } +}); \ No newline at end of file diff --git a/test/dialects/function-tests.js b/test/dialects/function-tests.js new file mode 100644 index 00000000..702d8c63 --- /dev/null +++ b/test/dialects/function-tests.js @@ -0,0 +1,82 @@ +'use strict'; + +var Harness = require('./support'); +var Sql = require('../../lib'); + +var post = Harness.definePostTable(); + +Harness.test({ + query: post.select(Sql.functions.LENGTH(post.content)), + pg: { + text : 'SELECT LENGTH("post"."content") FROM "post"', + string: 'SELECT LENGTH("post"."content") FROM "post"' + }, + sqlite: { + text : 'SELECT LENGTH("post"."content") FROM "post"', + string: 'SELECT LENGTH("post"."content") FROM "post"' + }, + mysql: { + text : 'SELECT LENGTH(`post`.`content`) FROM `post`', + string: 'SELECT LENGTH(`post`.`content`) FROM `post`' + }, + mssql: { + text : 'SELECT LEN([post].[content]) FROM [post]', + string: 'SELECT LEN([post].[content]) FROM [post]' + }, + oracle: { + text : 'SELECT LENGTH("post"."content") FROM "post"', + string: 'SELECT LENGTH("post"."content") FROM "post"' + }, + params: [] +}); + +Harness.test({ + query: post.select(Sql.functions.LEFT(post.content,4)), + pg: { + text : 'SELECT LEFT("post"."content", $1) FROM "post"', + string: 'SELECT LEFT("post"."content", 4) FROM "post"' + }, + sqlite: { + text : 'SELECT SUBSTR("post"."content", 1, $1) FROM "post"', + string: 'SELECT SUBSTR("post"."content", 1, 4) FROM "post"' + }, + mysql: { + text : 'SELECT LEFT(`post`.`content`, ?) FROM `post`', + string: 'SELECT LEFT(`post`.`content`, 4) FROM `post`' + }, + mssql: { + text : 'SELECT LEFT([post].[content], @1) FROM [post]', + string: 'SELECT LEFT([post].[content], 4) FROM [post]' + }, + oracle: { + text : 'SELECT LEFT("post"."content", :1) FROM "post"', + string: 'SELECT LEFT("post"."content", 4) FROM "post"' + }, + params: [4] +}); + +Harness.test({ + query: post.select(Sql.functions.RIGHT(post.content,4)), + pg: { + text : 'SELECT RIGHT("post"."content", $1) FROM "post"', + string: 'SELECT RIGHT("post"."content", 4) FROM "post"' + }, + sqlite: { + text : 'SELECT SUBSTR("post"."content", -$1) FROM "post"', + string: 'SELECT SUBSTR("post"."content", -4) FROM "post"' + }, + mysql: { + text : 'SELECT RIGHT(`post`.`content`, ?) FROM `post`', + string: 'SELECT RIGHT(`post`.`content`, 4) FROM `post`' + }, + mssql: { + text : 'SELECT RIGHT([post].[content], @1) FROM [post]', + string: 'SELECT RIGHT([post].[content], 4) FROM [post]' + }, + oracle: { + text : 'SELECT RIGHT("post"."content", :1) FROM "post"', + string: 'SELECT RIGHT("post"."content", 4) FROM "post"' + }, + params: [4] +}); + diff --git a/test/dialects/group-by-tests.js b/test/dialects/group-by-tests.js index c9db48ed..673b5639 100644 --- a/test/dialects/group-by-tests.js +++ b/test/dialects/group-by-tests.js @@ -17,6 +17,14 @@ Harness.test({ text : 'SELECT `post`.`content` FROM `post` GROUP BY `post`.`userId`', string: 'SELECT `post`.`content` FROM `post` GROUP BY `post`.`userId`' }, + mssql: { + text : 'SELECT [post].[content] FROM [post] GROUP BY [post].[userId]', + string: 'SELECT [post].[content] FROM [post] GROUP BY [post].[userId]' + }, + oracle: { + text : 'SELECT "post"."content" FROM "post" GROUP BY "post"."userId"', + string: 'SELECT "post"."content" FROM "post" GROUP BY "post"."userId"' + }, params: [] }); @@ -34,6 +42,14 @@ Harness.test({ text : 'SELECT `post`.`content` FROM `post` GROUP BY `post`.`userId`, `post`.`id`', string: 'SELECT `post`.`content` FROM `post` GROUP BY `post`.`userId`, `post`.`id`' }, + mssql: { + text : 'SELECT [post].[content] FROM [post] GROUP BY [post].[userId], [post].[id]', + string: 'SELECT [post].[content] FROM [post] GROUP BY [post].[userId], [post].[id]' + }, + oracle: { + text : 'SELECT "post"."content" FROM "post" GROUP BY "post"."userId", "post"."id"', + string: 'SELECT "post"."content" FROM "post" GROUP BY "post"."userId", "post"."id"' + }, params: [] }); @@ -51,6 +67,14 @@ Harness.test({ text : 'SELECT GROUP_CONCAT(`post`.`content`) AS `contents` FROM `post` GROUP BY `post`.`userId`', string: 'SELECT GROUP_CONCAT(`post`.`content`) AS `contents` FROM `post` GROUP BY `post`.`userId`' }, + mssql: { + text : 'SQL Server does not support array_agg.', + throws: true + }, + oracle: { + text : 'Oracle does not support array_agg.', + throws: true + }, params: [] }); @@ -68,6 +92,14 @@ Harness.test({ text : 'SELECT GROUP_CONCAT(`post`.`content`) AS `post contents` FROM `post` GROUP BY `post`.`userId`', string: 'SELECT GROUP_CONCAT(`post`.`content`) AS `post contents` FROM `post` GROUP BY `post`.`userId`' }, + mssql: { + text : 'SQL Server does not support array_agg.', + throws: true + }, + oracle: { + text : 'Oracle does not support array_agg.', + throws: true + }, params: [] }); @@ -85,5 +117,13 @@ Harness.test({ text : 'SELECT `post`.`content` FROM `post` GROUP BY `post`.`userId`, `post`.`id`', string: 'SELECT `post`.`content` FROM `post` GROUP BY `post`.`userId`, `post`.`id`' }, + mssql: { + text : 'SELECT [post].[content] FROM [post] GROUP BY [post].[userId], [post].[id]', + string: 'SELECT [post].[content] FROM [post] GROUP BY [post].[userId], [post].[id]' + }, + oracel: { + text : 'SELECT "post"."content" FROM "post" GROUP BY "post"."userId", "post"."id"', + string: 'SELECT "post"."content" FROM "post" GROUP BY "post"."userId", "post"."id"' + }, params: [] }); diff --git a/test/dialects/having-tests.js b/test/dialects/having-tests.js index b06190e3..5f8f9cc7 100644 --- a/test/dialects/having-tests.js +++ b/test/dialects/having-tests.js @@ -17,6 +17,14 @@ Harness.test({ text : 'SELECT `post`.`userId`, COUNT(`post`.`content`) AS `content_count` FROM `post` GROUP BY `post`.`userId` HAVING (`post`.`userId` > ?)', string: 'SELECT `post`.`userId`, COUNT(`post`.`content`) AS `content_count` FROM `post` GROUP BY `post`.`userId` HAVING (`post`.`userId` > 10)' }, + mssql : { + text : 'SELECT [post].[userId], COUNT([post].[content]) AS [content_count] FROM [post] GROUP BY [post].[userId] HAVING ([post].[userId] > @1)', + string: 'SELECT [post].[userId], COUNT([post].[content]) AS [content_count] FROM [post] GROUP BY [post].[userId] HAVING ([post].[userId] > 10)' + }, + oracle: { + text : 'SELECT "post"."userId", COUNT("post"."content") "content_count" FROM "post" GROUP BY "post"."userId" HAVING ("post"."userId" > :1)', + string: 'SELECT "post"."userId", COUNT("post"."content") "content_count" FROM "post" GROUP BY "post"."userId" HAVING ("post"."userId" > 10)' + }, params: [10] }); @@ -34,6 +42,14 @@ Harness.test({ text : 'SELECT `post`.`userId`, COUNT(`post`.`content`) AS `content_count` FROM `post` GROUP BY `post`.`userId` HAVING (`post`.`userId` > ?) AND (`post`.`userId` < ?)', string: 'SELECT `post`.`userId`, COUNT(`post`.`content`) AS `content_count` FROM `post` GROUP BY `post`.`userId` HAVING (`post`.`userId` > 10) AND (`post`.`userId` < 100)' }, + mssql : { + text : 'SELECT [post].[userId], COUNT([post].[content]) AS [content_count] FROM [post] GROUP BY [post].[userId] HAVING ([post].[userId] > @1) AND ([post].[userId] < @2)', + string: 'SELECT [post].[userId], COUNT([post].[content]) AS [content_count] FROM [post] GROUP BY [post].[userId] HAVING ([post].[userId] > 10) AND ([post].[userId] < 100)' + }, + oracle: { + text : 'SELECT "post"."userId", COUNT("post"."content") "content_count" FROM "post" GROUP BY "post"."userId" HAVING ("post"."userId" > :1) AND ("post"."userId" < :2)', + string: 'SELECT "post"."userId", COUNT("post"."content") "content_count" FROM "post" GROUP BY "post"."userId" HAVING ("post"."userId" > 10) AND ("post"."userId" < 100)' + }, params: [10, 100] }); @@ -51,5 +67,13 @@ Harness.test({ text : 'SELECT `post`.`userId`, COUNT(`post`.`content`) AS `content_count` FROM `post` GROUP BY `post`.`userId` HAVING (`post`.`userId` > ?) AND (`post`.`userId` < ?)', string: 'SELECT `post`.`userId`, COUNT(`post`.`content`) AS `content_count` FROM `post` GROUP BY `post`.`userId` HAVING (`post`.`userId` > 10) AND (`post`.`userId` < 100)' }, + mssql : { + text : 'SELECT [post].[userId], COUNT([post].[content]) AS [content_count] FROM [post] GROUP BY [post].[userId] HAVING ([post].[userId] > @1) AND ([post].[userId] < @2)', + string: 'SELECT [post].[userId], COUNT([post].[content]) AS [content_count] FROM [post] GROUP BY [post].[userId] HAVING ([post].[userId] > 10) AND ([post].[userId] < 100)' + }, + oracle: { + text : 'SELECT "post"."userId", COUNT("post"."content") "content_count" FROM "post" GROUP BY "post"."userId" HAVING ("post"."userId" > :1) AND ("post"."userId" < :2)', + string: 'SELECT "post"."userId", COUNT("post"."content") "content_count" FROM "post" GROUP BY "post"."userId" HAVING ("post"."userId" > 10) AND ("post"."userId" < 100)' + }, params: [10, 100] }); diff --git a/test/dialects/hstore-tests.js b/test/dialects/hstore-tests.js new file mode 100644 index 00000000..54ba2d22 --- /dev/null +++ b/test/dialects/hstore-tests.js @@ -0,0 +1,25 @@ +'use strict'; + +var Harness = require('./support'); +var customer = Harness.defineCustomerTable(); +var Sql = require('../../lib').setDialect('postgres'); + +Harness.test({ + query: customer.update({ + metadata: customer.metadata.concat(Sql.functions.HSTORE('age', 20)) + }), + pg: { + text : 'UPDATE "customer" SET "metadata" = ("customer"."metadata" || HSTORE($1, $2))', + string: 'UPDATE "customer" SET "metadata" = ("customer"."metadata" || HSTORE(\'age\', 20))' + }, + params: ['age', 20] +}); + +Harness.test({ + query: customer.select(customer.metadata.key('age')), + pg: { + text : 'SELECT ("customer"."metadata" -> $1) FROM "customer"', + string: 'SELECT ("customer"."metadata" -> \'age\') FROM "customer"' + }, + params: ['age'] +}); diff --git a/test/dialects/ilike-tests.js b/test/dialects/ilike-tests.js new file mode 100644 index 00000000..aba487ee --- /dev/null +++ b/test/dialects/ilike-tests.js @@ -0,0 +1,45 @@ +'use strict'; + +var Harness = require('./support'); +var post = Harness.definePostTable(); +var user = Harness.defineUserTable(); +var Sql = require('../../lib').setDialect('postgres'); + +Harness.test({ + query: post.select(post.content, post.userId).where(post.content.ilike('A%')), + pg: { + text : 'SELECT "post"."content", "post"."userId" FROM "post" WHERE ("post"."content" ILIKE $1)', + string: 'SELECT "post"."content", "post"."userId" FROM "post" WHERE ("post"."content" ILIKE \'A%\')' + }, + params: ['A%'] +}); + +Harness.test({ + query: post.insert(post.content, post.userId) + .select('\'test\'', user.id).from(user).where(user.name.ilike('A%')), + pg: { + text : 'INSERT INTO "post" ("content", "userId") SELECT \'test\', "user"."id" FROM "user" WHERE ("user"."name" ILIKE $1)', + string: 'INSERT INTO "post" ("content", "userId") SELECT \'test\', "user"."id" FROM "user" WHERE ("user"."name" ILIKE \'A%\')' + }, + params: ['A%'] +}); + +Harness.test({ + query: post.insert([post.content, post.userId]) + .select('\'test\'', user.id).from(user).where(user.name.ilike('A%')), + pg: { + text : 'INSERT INTO "post" ("content", "userId") SELECT \'test\', "user"."id" FROM "user" WHERE ("user"."name" ILIKE $1)', + string: 'INSERT INTO "post" ("content", "userId") SELECT \'test\', "user"."id" FROM "user" WHERE ("user"."name" ILIKE \'A%\')' + }, + params: ['A%'] +}); + +Harness.test({ + query: post.insert(post.userId) + .select(user.id).from(user).where(user.name.ilike('A%')), + pg: { + text : 'INSERT INTO "post" ("userId") SELECT "user"."id" FROM "user" WHERE ("user"."name" ILIKE $1)', + string: 'INSERT INTO "post" ("userId") SELECT "user"."id" FROM "user" WHERE ("user"."name" ILIKE \'A%\')' + }, + params: ['A%'] +}); diff --git a/test/dialects/in-clause-tests.js b/test/dialects/in-clause-tests.js new file mode 100644 index 00000000..513c7243 --- /dev/null +++ b/test/dialects/in-clause-tests.js @@ -0,0 +1,179 @@ +'use strict'; + +var Harness = require('./support'); +var post = Harness.definePostTable(); + +Harness.test({ + query: post.select(post.star()).where(post.id.in([])), + pg: { + text : 'SELECT "post".* FROM "post" WHERE (1=0)', + string: 'SELECT "post".* FROM "post" WHERE (1=0)' + }, + sqlite: { + text : 'SELECT "post".* FROM "post" WHERE (1=0)', + string: 'SELECT "post".* FROM "post" WHERE (1=0)' + }, + mysql: { + text : 'SELECT `post`.* FROM `post` WHERE (1=0)', + string: 'SELECT `post`.* FROM `post` WHERE (1=0)' + }, + mssql: { + text : 'SELECT [post].* FROM [post] WHERE (1=0)', + string: 'SELECT [post].* FROM [post] WHERE (1=0)' + }, + oracle: { + text : 'SELECT "post".* FROM "post" WHERE (1=0)', + string: 'SELECT "post".* FROM "post" WHERE (1=0)' + }, + params: [] +}); + +Harness.test({ + query: post.select(post.star()).where(post.id.in([1])), + pg: { + text : 'SELECT "post".* FROM "post" WHERE ("post"."id" IN ($1))', + string: 'SELECT "post".* FROM "post" WHERE ("post"."id" IN (1))' + }, + sqlite: { + text : 'SELECT "post".* FROM "post" WHERE ("post"."id" IN ($1))', + string: 'SELECT "post".* FROM "post" WHERE ("post"."id" IN (1))' + }, + mysql: { + text : 'SELECT `post`.* FROM `post` WHERE (`post`.`id` IN (?))', + string: 'SELECT `post`.* FROM `post` WHERE (`post`.`id` IN (1))' + }, + mssql: { + text : 'SELECT [post].* FROM [post] WHERE ([post].[id] IN (@1))', + string: 'SELECT [post].* FROM [post] WHERE ([post].[id] IN (1))' + }, + oracle: { + text : 'SELECT "post".* FROM "post" WHERE ("post"."id" IN (:1))', + string: 'SELECT "post".* FROM "post" WHERE ("post"."id" IN (1))' + }, + params: [1] +}); + +Harness.test({ + query: post.select(post.star()).where(post.id.in([null])), + pg: { + text : 'SELECT "post".* FROM "post" WHERE ("post"."id" IS NULL)', + string: 'SELECT "post".* FROM "post" WHERE ("post"."id" IS NULL)' + }, + sqlite: { + text : 'SELECT "post".* FROM "post" WHERE ("post"."id" IS NULL)', + string: 'SELECT "post".* FROM "post" WHERE ("post"."id" IS NULL)' + }, + mysql: { + text : 'SELECT `post`.* FROM `post` WHERE (`post`.`id` IS NULL)', + string: 'SELECT `post`.* FROM `post` WHERE (`post`.`id` IS NULL)' + }, + mssql: { + text : 'SELECT [post].* FROM [post] WHERE ([post].[id] IS NULL)', + string: 'SELECT [post].* FROM [post] WHERE ([post].[id] IS NULL)' + }, + oracle: { + text : 'SELECT "post".* FROM "post" WHERE ("post"."id" IS NULL)', + string: 'SELECT "post".* FROM "post" WHERE ("post"."id" IS NULL)' + }, + params: [] +}); + +Harness.test({ + query: post.select(post.star()).where(post.id.in([1, 2])), + pg: { + text : 'SELECT "post".* FROM "post" WHERE ("post"."id" IN ($1, $2))', + string: 'SELECT "post".* FROM "post" WHERE ("post"."id" IN (1, 2))' + }, + sqlite: { + text : 'SELECT "post".* FROM "post" WHERE ("post"."id" IN ($1, $2))', + string: 'SELECT "post".* FROM "post" WHERE ("post"."id" IN (1, 2))' + }, + mysql: { + text : 'SELECT `post`.* FROM `post` WHERE (`post`.`id` IN (?, ?))', + string: 'SELECT `post`.* FROM `post` WHERE (`post`.`id` IN (1, 2))' + }, + mssql: { + text : 'SELECT [post].* FROM [post] WHERE ([post].[id] IN (@1, @2))', + string: 'SELECT [post].* FROM [post] WHERE ([post].[id] IN (1, 2))' + }, + oracle: { + text : 'SELECT "post".* FROM "post" WHERE ("post"."id" IN (:1, :2))', + string: 'SELECT "post".* FROM "post" WHERE ("post"."id" IN (1, 2))' + }, + params: [1, 2] +}); + +Harness.test({ + query: post.select(post.star()).where(post.id.in([null, null])), + pg: { + text : 'SELECT "post".* FROM "post" WHERE ("post"."id" IS NULL)', + string: 'SELECT "post".* FROM "post" WHERE ("post"."id" IS NULL)' + }, + sqlite: { + text : 'SELECT "post".* FROM "post" WHERE ("post"."id" IS NULL)', + string: 'SELECT "post".* FROM "post" WHERE ("post"."id" IS NULL)' + }, + mysql: { + text : 'SELECT `post`.* FROM `post` WHERE (`post`.`id` IS NULL)', + string: 'SELECT `post`.* FROM `post` WHERE (`post`.`id` IS NULL)' + }, + mssql: { + text : 'SELECT [post].* FROM [post] WHERE ([post].[id] IS NULL)', + string: 'SELECT [post].* FROM [post] WHERE ([post].[id] IS NULL)' + }, + oracle: { + text : 'SELECT "post".* FROM "post" WHERE ("post"."id" IS NULL)', + string: 'SELECT "post".* FROM "post" WHERE ("post"."id" IS NULL)' + }, + params: [] +}); + +Harness.test({ + query: post.select(post.star()).where(post.id.in([1, null, 2])), + pg: { + text : 'SELECT "post".* FROM "post" WHERE ("post"."id" IN ($1, $2) OR "post"."id" IS NULL)', + string: 'SELECT "post".* FROM "post" WHERE ("post"."id" IN (1, 2) OR "post"."id" IS NULL)' + }, + sqlite: { + text : 'SELECT "post".* FROM "post" WHERE ("post"."id" IN ($1, $2) OR "post"."id" IS NULL)', + string: 'SELECT "post".* FROM "post" WHERE ("post"."id" IN (1, 2) OR "post"."id" IS NULL)' + }, + mysql: { + text : 'SELECT `post`.* FROM `post` WHERE (`post`.`id` IN (?, ?) OR `post`.`id` IS NULL)', + string: 'SELECT `post`.* FROM `post` WHERE (`post`.`id` IN (1, 2) OR `post`.`id` IS NULL)' + }, + mssql: { + text : 'SELECT [post].* FROM [post] WHERE ([post].[id] IN (@1, @2) OR [post].[id] IS NULL)', + string: 'SELECT [post].* FROM [post] WHERE ([post].[id] IN (1, 2) OR [post].[id] IS NULL)' + }, + oracle: { + text : 'SELECT "post".* FROM "post" WHERE ("post"."id" IN (:1, :2) OR "post"."id" IS NULL)', + string: 'SELECT "post".* FROM "post" WHERE ("post"."id" IN (1, 2) OR "post"."id" IS NULL)' + }, + params: [1, 2] +}); + +Harness.test({ + query: post.select(post.star()).where(post.id.in([1, null, 2, null])), + pg: { + text : 'SELECT "post".* FROM "post" WHERE ("post"."id" IN ($1, $2) OR "post"."id" IS NULL)', + string: 'SELECT "post".* FROM "post" WHERE ("post"."id" IN (1, 2) OR "post"."id" IS NULL)' + }, + sqlite: { + text : 'SELECT "post".* FROM "post" WHERE ("post"."id" IN ($1, $2) OR "post"."id" IS NULL)', + string: 'SELECT "post".* FROM "post" WHERE ("post"."id" IN (1, 2) OR "post"."id" IS NULL)' + }, + mysql: { + text : 'SELECT `post`.* FROM `post` WHERE (`post`.`id` IN (?, ?) OR `post`.`id` IS NULL)', + string: 'SELECT `post`.* FROM `post` WHERE (`post`.`id` IN (1, 2) OR `post`.`id` IS NULL)' + }, + mssql: { + text : 'SELECT [post].* FROM [post] WHERE ([post].[id] IN (@1, @2) OR [post].[id] IS NULL)', + string: 'SELECT [post].* FROM [post] WHERE ([post].[id] IN (1, 2) OR [post].[id] IS NULL)' + }, + oracle: { + text : 'SELECT "post".* FROM "post" WHERE ("post"."id" IN (:1, :2) OR "post"."id" IS NULL)', + string: 'SELECT "post".* FROM "post" WHERE ("post"."id" IN (1, 2) OR "post"."id" IS NULL)' + }, + params: [1, 2] +}); diff --git a/test/dialects/indexes-tests.js b/test/dialects/indexes-tests.js index 916b4eef..86b50449 100644 --- a/test/dialects/indexes-tests.js +++ b/test/dialects/indexes-tests.js @@ -6,8 +6,8 @@ var post = Harness.definePostTable(); Harness.test({ query: post.indexes(), pg: { - text : 'SELECT relname FROM pg_class WHERE oid IN ( SELECT indexrelid FROM pg_index, pg_class WHERE pg_class.relname="post" AND pg_class.oid=pg_index.indrelid)', - string: 'SELECT relname FROM pg_class WHERE oid IN ( SELECT indexrelid FROM pg_index, pg_class WHERE pg_class.relname="post" AND pg_class.oid=pg_index.indrelid)' + text : 'SELECT relname FROM pg_class WHERE oid IN ( SELECT indexrelid FROM pg_index, pg_class WHERE pg_class.relname=\'post\' AND pg_class.relnamespace IN (SELECT pg_namespace.oid FROM pg_namespace WHERE nspname = \'public\') AND pg_class.oid=pg_index.indrelid)', + string: 'SELECT relname FROM pg_class WHERE oid IN ( SELECT indexrelid FROM pg_index, pg_class WHERE pg_class.relname=\'post\' AND pg_class.relnamespace IN (SELECT pg_namespace.oid FROM pg_namespace WHERE nspname = \'public\') AND pg_class.oid=pg_index.indrelid)' }, mysql: { text : 'SHOW INDEX FROM `post`', @@ -17,6 +17,10 @@ Harness.test({ text : 'PRAGMA INDEX_LIST("post")', string: 'PRAGMA INDEX_LIST("post")' }, + oracle: { + text : 'SELECT * FROM USER_INDEXES WHERE TABLE_NAME = \'post\'', + string: 'SELECT * FROM USER_INDEXES WHERE TABLE_NAME = \'post\'' + }, params: [] }); @@ -34,6 +38,10 @@ Harness.test({ text : 'CREATE UNIQUE INDEX "index_name" USING BTREE ON "post" ("id","userId") WITH PARSER foo', string: 'CREATE UNIQUE INDEX "index_name" USING BTREE ON "post" ("id","userId") WITH PARSER foo' }, + oracle: { + text : 'CREATE UNIQUE INDEX "index_name" USING BTREE ON "post" ("id","userId") WITH PARSER foo', + string: 'CREATE UNIQUE INDEX "index_name" USING BTREE ON "post" ("id","userId") WITH PARSER foo' + }, params: [] }); @@ -85,6 +93,10 @@ Harness.test({ text : 'CREATE INDEX "post_id_userId" ON "post" ("userId","id")', string: 'CREATE INDEX "post_id_userId" ON "post" ("userId","id")' }, + oracle: { + text : 'CREATE INDEX "post_id_userId" ON "post" ("userId","id")', + string: 'CREATE INDEX "post_id_userId" ON "post" ("userId","id")' + }, params: [] }); @@ -102,9 +114,64 @@ Harness.test({ text : 'CREATE INDEX "post_id_userId" ON "post" ("userId","id")', string: 'CREATE INDEX "post_id_userId" ON "post" ("userId","id")' }, + oracle: { + text : 'CREATE INDEX "post_id_userId" ON "post" ("userId","id")', + string: 'CREATE INDEX "post_id_userId" ON "post" ("userId","id")' + }, + params: [] +}); + +Harness.test({ + query: post.indexes().create().on(post.userId, post.id.desc), + pg: { + text : 'CREATE INDEX "post_id_userId" ON "post" ("userId","id" DESC)', + string: 'CREATE INDEX "post_id_userId" ON "post" ("userId","id" DESC)' + }, + mysql: { + text : 'CREATE INDEX `post_id_userId` ON `post` (`userId`,`id` DESC)', + string: 'CREATE INDEX `post_id_userId` ON `post` (`userId`,`id` DESC)' + }, + sqlite: { + text : 'CREATE INDEX "post_id_userId" ON "post" ("userId","id" DESC)', + string: 'CREATE INDEX "post_id_userId" ON "post" ("userId","id" DESC)' + }, + mssql: { + text : 'CREATE INDEX [post_id_userId] ON [post] ([userId],[id] DESC)', + string: 'CREATE INDEX [post_id_userId] ON [post] ([userId],[id] DESC)' + }, + oracle: { + text : 'CREATE INDEX "post_id_userId" ON "post" ("userId","id" DESC)', + string: 'CREATE INDEX "post_id_userId" ON "post" ("userId","id" DESC)' + }, + params: [] +}); + +Harness.test({ + query: post.indexes().create().on(post.userId).on(post.id.descending), + pg: { + text : 'CREATE INDEX "post_id_userId" ON "post" ("userId","id" DESC)', + string: 'CREATE INDEX "post_id_userId" ON "post" ("userId","id" DESC)' + }, + mysql: { + text : 'CREATE INDEX `post_id_userId` ON `post` (`userId`,`id` DESC)', + string: 'CREATE INDEX `post_id_userId` ON `post` (`userId`,`id` DESC)' + }, + sqlite: { + text : 'CREATE INDEX "post_id_userId" ON "post" ("userId","id" DESC)', + string: 'CREATE INDEX "post_id_userId" ON "post" ("userId","id" DESC)' + }, + mssql: { + text : 'CREATE INDEX [post_id_userId] ON [post] ([userId],[id] DESC)', + string: 'CREATE INDEX [post_id_userId] ON [post] ([userId],[id] DESC)' + }, + oracle: { + text : 'CREATE INDEX "post_id_userId" ON "post" ("userId","id" DESC)', + string: 'CREATE INDEX "post_id_userId" ON "post" ("userId","id" DESC)' + }, params: [] }); + Harness.test({ query: post.indexes().create(), pg: { @@ -118,22 +185,30 @@ Harness.test({ sqlite: { text : 'No columns defined!', throws: true + }, + oracle: { + text : 'No columns defined!', + throws: true } }); Harness.test({ query: post.indexes().drop('index_name'), pg: { - text : 'DROP INDEX "index_name" ON "post"', - string: 'DROP INDEX "index_name" ON "post"' + text : 'DROP INDEX "public"."index_name"', + string: 'DROP INDEX "public"."index_name"' }, mysql: { - text : 'DROP INDEX `index_name` ON `post`', - string: 'DROP INDEX `index_name` ON `post`' + text : 'DROP INDEX `public`.`index_name`', + string: 'DROP INDEX `public`.`index_name`' }, sqlite: { - text : 'DROP INDEX "index_name" ON "post"', - string: 'DROP INDEX "index_name" ON "post"' + text : 'DROP INDEX "public"."index_name"', + string: 'DROP INDEX "public"."index_name"' + }, + oracle: { + text : 'DROP INDEX "index_name"', + string: 'DROP INDEX "index_name"' }, params: [] }); @@ -141,16 +216,20 @@ Harness.test({ Harness.test({ query: post.indexes().drop(post.userId, post.id), pg: { - text : 'DROP INDEX "post_id_userId" ON "post"', - string: 'DROP INDEX "post_id_userId" ON "post"' + text : 'DROP INDEX "public"."post_id_userId"', + string: 'DROP INDEX "public"."post_id_userId"' }, mysql: { - text : 'DROP INDEX `post_id_userId` ON `post`', - string: 'DROP INDEX `post_id_userId` ON `post`' + text : 'DROP INDEX `public`.`post_id_userId`', + string: 'DROP INDEX `public`.`post_id_userId`' }, sqlite: { - text : 'DROP INDEX "post_id_userId" ON "post"', - string: 'DROP INDEX "post_id_userId" ON "post"' + text : 'DROP INDEX "public"."post_id_userId"', + string: 'DROP INDEX "public"."post_id_userId"' + }, + oracle: { + text : 'DROP INDEX "post_id_userId"', + string: 'DROP INDEX "post_id_userId"' }, params: [] }); diff --git a/test/dialects/insert-tests.js b/test/dialects/insert-tests.js index 8340faf4..60ba6a62 100644 --- a/test/dialects/insert-tests.js +++ b/test/dialects/insert-tests.js @@ -2,6 +2,14 @@ var Harness = require('./support'); var post = Harness.definePostTable(); +var user = Harness.defineUserTable(); +var contentTable = Harness.defineContentTable(); +var customerAliasTable = Harness.defineCustomerAliasTable(); + +var arrayTable = require('../../lib/table').define({ + name: 'arraytest', + columns: ['id', 'numbers'] +}); Harness.test({ query: post.insert(post.content.value('test'), post.userId.value(1)), @@ -17,10 +25,13 @@ Harness.test({ text : 'INSERT INTO `post` (`content`, `userId`) VALUES (?, ?)', string: 'INSERT INTO `post` (`content`, `userId`) VALUES (\'test\', 1)' }, + oracle: { + text : 'INSERT INTO "post" ("content", "userId") VALUES (:1, :2)', + string: 'INSERT INTO "post" ("content", "userId") VALUES (\'test\', 1)' + }, params: ['test', 1] }); - Harness.test({ query: post.insert(post.content.value('whoah')), pg: { @@ -35,9 +46,42 @@ Harness.test({ text : 'INSERT INTO `post` (`content`) VALUES (?)', string: 'INSERT INTO `post` (`content`) VALUES (\'whoah\')' }, + mssql: { + text : 'INSERT INTO [post] ([content]) VALUES (@1)', + string: 'INSERT INTO [post] ([content]) VALUES (\'whoah\')' + }, + oracle: { + text : 'INSERT INTO "post" ("content") VALUES (:1)', + string: 'INSERT INTO "post" ("content") VALUES (\'whoah\')' + }, params: ['whoah'] }); +Harness.test({ + query: post.insert({length: 0}), + pg: { + text : 'INSERT INTO "post" ("length") VALUES ($1)', + string: 'INSERT INTO "post" ("length") VALUES (0)' + }, + sqlite: { + text : 'INSERT INTO "post" ("length") VALUES ($1)', + string: 'INSERT INTO "post" ("length") VALUES (0)' + }, + mysql: { + text : 'INSERT INTO `post` (`length`) VALUES (?)', + string: 'INSERT INTO `post` (`length`) VALUES (0)' + }, + mssql: { + text : 'INSERT INTO [post] ([length]) VALUES (@1)', + string: 'INSERT INTO [post] ([length]) VALUES (0)' + }, + oracle: { + text : 'INSERT INTO "post" ("length") VALUES (:1)', + string: 'INSERT INTO "post" ("length") VALUES (0)' + }, + params: [0] +}); + Harness.test({ query: post.insert({ content: 'test', @@ -55,9 +99,37 @@ Harness.test({ text : 'INSERT INTO `post` (`content`, `userId`) VALUES (?, ?)', string: 'INSERT INTO `post` (`content`, `userId`) VALUES (\'test\', 2)' }, + oracle: { + text : 'INSERT INTO "post" ("content", "userId") VALUES (:1, :2)', + string: 'INSERT INTO "post" ("content", "userId") VALUES (\'test\', 2)' + }, params: ['test', 2] }); +Harness.test({ + query: post.insert({ + content: post.sql.functions.LOWER('TEST'), + userId: 2 + }), + pg: { + text : 'INSERT INTO "post" ("content", "userId") VALUES (LOWER($1), $2)', + string: 'INSERT INTO "post" ("content", "userId") VALUES (LOWER(\'TEST\'), 2)' + }, + sqlite: { + text : 'INSERT INTO "post" ("content", "userId") VALUES (LOWER($1), $2)', + string: 'INSERT INTO "post" ("content", "userId") VALUES (LOWER(\'TEST\'), 2)' + }, + mysql: { + text : 'INSERT INTO `post` (`content`, `userId`) VALUES (LOWER(?), ?)', + string: 'INSERT INTO `post` (`content`, `userId`) VALUES (LOWER(\'TEST\'), 2)' + }, + oracle: { + text : 'INSERT INTO "post" ("content", "userId") VALUES (LOWER(:1), :2)', + string: 'INSERT INTO "post" ("content", "userId") VALUES (LOWER(\'TEST\'), 2)' + }, + params: ['TEST', 2] +}); + // allow bulk insert Harness.test({ query: post.insert([{ @@ -78,6 +150,10 @@ Harness.test({ text : 'INSERT INTO `post` (`content`) VALUES (?), (?)', string: 'INSERT INTO `post` (`content`) VALUES (\'whoah\'), (\'hey\')' }, + oracle: { + text : 'INSERT INTO "post" ("content") VALUES (:1), (:2)', + string: 'INSERT INTO "post" ("content") VALUES (\'whoah\'), (\'hey\')' + }, params: ['whoah', 'hey'] }); @@ -102,6 +178,10 @@ Harness.test({ text : 'INSERT INTO `post` (`content`, `userId`) VALUES (?, ?), (?, ?)', string: 'INSERT INTO `post` (`content`, `userId`) VALUES (\'whoah\', 1), (\'hey\', 2)' }, + oracle: { + text : 'INSERT INTO "post" ("content", "userId") VALUES (:1, :2), (:3, :4)', + string: 'INSERT INTO "post" ("content", "userId") VALUES (\'whoah\', 1), (\'hey\', 2)' + }, params: ['whoah', 1, 'hey', 2] }); @@ -127,6 +207,14 @@ Harness.test({ text : 'INSERT INTO `post` (`content`, `userId`) VALUES (?, ?), (?, ?)', string: 'INSERT INTO `post` (`content`, `userId`) VALUES (\'whoah\', 1), (\'hey\', 2)' }, + mssql: { + text : 'INSERT INTO [post] ([content], [userId]) VALUES (@1, @2), (@3, @4)', + string: 'INSERT INTO [post] ([content], [userId]) VALUES (\'whoah\', 1), (\'hey\', 2)' + }, + oracle: { + text : 'INSERT INTO "post" ("content", "userId") VALUES (:1, :2), (:3, :4)', + string: 'INSERT INTO "post" ("content", "userId") VALUES (\'whoah\', 1), (\'hey\', 2)' + }, params: ['whoah', 1, 'hey', 2] }); @@ -144,6 +232,14 @@ Harness.test({ text : 'INSERT INTO `post` () VALUES ()', string: 'INSERT INTO `post` () VALUES ()' }, + mssql: { + text : 'INSERT INTO [post] DEFAULT VALUES', + string: 'INSERT INTO [post] DEFAULT VALUES' + }, + oracle: { + text : 'INSERT INTO "post" DEFAULT VALUES', + string: 'INSERT INTO "post" DEFAULT VALUES' + }, params: [] }); @@ -154,12 +250,16 @@ Harness.test({ string: 'INSERT INTO "post" DEFAULT VALUES RETURNING *' }, sqlite: { - text : 'INSERT INTO "post" DEFAULT VALUES RETURNING *', - string: 'INSERT INTO "post" DEFAULT VALUES RETURNING *' + throws: true }, mysql: { - text : 'INSERT INTO `post` () VALUES () RETURNING *', - string: 'INSERT INTO `post` () VALUES () RETURNING *' + throws: true + }, + mssql: { + throws: true + }, + oracle: { + throws: true }, params: [] }); @@ -171,12 +271,16 @@ Harness.test({ string: 'INSERT INTO "post" DEFAULT VALUES RETURNING *' }, sqlite: { - text : 'INSERT INTO "post" DEFAULT VALUES RETURNING *', - string: 'INSERT INTO "post" DEFAULT VALUES RETURNING *' + throws: true }, mysql: { - text : 'INSERT INTO `post` () VALUES () RETURNING *', - string: 'INSERT INTO `post` () VALUES () RETURNING *' + throws: true + }, + mssql: { + throws: true + }, + oracle: { + throws: true }, params: [] }); @@ -188,12 +292,16 @@ Harness.test({ string: 'INSERT INTO "post" DEFAULT VALUES RETURNING "id"' }, sqlite: { - text : 'INSERT INTO "post" DEFAULT VALUES RETURNING "id"', - string: 'INSERT INTO "post" DEFAULT VALUES RETURNING "id"' + throws: true }, mysql: { - text : 'INSERT INTO `post` () VALUES () RETURNING `id`', - string: 'INSERT INTO `post` () VALUES () RETURNING `id`' + throws: true + }, + mssql: { + throws: true + }, + oracle: { + throws: true }, params: [] }); @@ -205,12 +313,16 @@ Harness.test({ string: 'INSERT INTO "post" DEFAULT VALUES RETURNING "id", "content"' }, sqlite: { - text : 'INSERT INTO "post" DEFAULT VALUES RETURNING "id", "content"', - string: 'INSERT INTO "post" DEFAULT VALUES RETURNING "id", "content"' + throws: true }, mysql: { - text : 'INSERT INTO `post` () VALUES () RETURNING `id`, `content`', - string: 'INSERT INTO `post` () VALUES () RETURNING `id`, `content`' + throws: true + }, + mssql: { + throws: true + }, + oracle: { + throws: true }, params: [] }); @@ -222,12 +334,16 @@ Harness.test({ string: 'INSERT INTO "post" DEFAULT VALUES RETURNING "id", "content"' }, sqlite: { - text : 'INSERT INTO "post" DEFAULT VALUES RETURNING "id", "content"', - string: 'INSERT INTO "post" DEFAULT VALUES RETURNING "id", "content"' + throws: true }, mysql: { - text : 'INSERT INTO `post` () VALUES () RETURNING `id`, `content`', - string: 'INSERT INTO `post` () VALUES () RETURNING `id`, `content`' + throws: true + }, + mssql: { + throws: true + }, + oracle: { + throws: true }, params: [] }); @@ -254,7 +370,17 @@ Harness.test({ text : 'INSERT INTO `post` (`content`, `userId`) VALUES (?, ?), (?, DEFAULT)', string: 'INSERT INTO `post` (`content`, `userId`) VALUES (\'whoah\', 1), (\'hey\', DEFAULT)', params: ['whoah', 1, 'hey'] - } + }, + mssql: { + text : 'INSERT INTO [post] ([content], [userId]) VALUES (@1, @2), (@3, DEFAULT)', + string: 'INSERT INTO [post] ([content], [userId]) VALUES (\'whoah\', 1), (\'hey\', DEFAULT)', + params: ['whoah', 1, 'hey'] + }, + oracle: { + text : 'INSERT INTO "post" ("content", "userId") VALUES (:1, :2), (:3, DEFAULT)', + string: 'INSERT INTO "post" ("content", "userId") VALUES (\'whoah\', 1), (\'hey\', DEFAULT)', + params: ['whoah', 1, 'hey'] + }, }); Harness.test({ @@ -278,5 +404,691 @@ Harness.test({ text : 'INSERT INTO `post` (`userId`, `content`) VALUES (?, DEFAULT), (?, ?)', string: 'INSERT INTO `post` (`userId`, `content`) VALUES (1, DEFAULT), (2, \'hey\')', params: [1, 2, 'hey'] + }, + mssql: { + text : 'INSERT INTO [post] ([userId], [content]) VALUES (@1, DEFAULT), (@2, @3)', + string: 'INSERT INTO [post] ([userId], [content]) VALUES (1, DEFAULT), (2, \'hey\')', + params: [1, 2, 'hey'] + }, + oracle: { + text : 'INSERT INTO "post" ("userId", "content") VALUES (:1, DEFAULT), (:2, :3)', + string: 'INSERT INTO "post" ("userId", "content") VALUES (1, DEFAULT), (2, \'hey\')', + params: [1, 2, 'hey'] + } +}); + +Harness.test({ + query: post.insert(post.content, post.userId) + .select('\'test\'', user.id).from(user).where(user.name.like('A%')), + pg: { + text : 'INSERT INTO "post" ("content", "userId") SELECT \'test\', "user"."id" FROM "user" WHERE ("user"."name" LIKE $1)', + string: 'INSERT INTO "post" ("content", "userId") SELECT \'test\', "user"."id" FROM "user" WHERE ("user"."name" LIKE \'A%\')' + }, + sqlite: { + text : 'INSERT INTO "post" ("content", "userId") SELECT \'test\', "user"."id" FROM "user" WHERE ("user"."name" LIKE $1)', + string: 'INSERT INTO "post" ("content", "userId") SELECT \'test\', "user"."id" FROM "user" WHERE ("user"."name" LIKE \'A%\')' + }, + mysql: { + text : 'INSERT INTO `post` (`content`, `userId`) SELECT \'test\', `user`.`id` FROM `user` WHERE (`user`.`name` LIKE ?)', + string: 'INSERT INTO `post` (`content`, `userId`) SELECT \'test\', `user`.`id` FROM `user` WHERE (`user`.`name` LIKE \'A%\')' + }, + mssql: { + text : 'INSERT INTO [post] ([content], [userId]) SELECT \'test\', [user].[id] FROM [user] WHERE ([user].[name] LIKE @1)', + string: 'INSERT INTO [post] ([content], [userId]) SELECT \'test\', [user].[id] FROM [user] WHERE ([user].[name] LIKE \'A%\')' + }, + oracle: { + text : 'INSERT INTO "post" ("content", "userId") SELECT \'test\', "user"."id" FROM "user" WHERE ("user"."name" LIKE :1)', + string: 'INSERT INTO "post" ("content", "userId") SELECT \'test\', "user"."id" FROM "user" WHERE ("user"."name" LIKE \'A%\')' + }, + params: ['A%'] +}); + +Harness.test({ + query: post.insert([post.content, post.userId]) + .select('\'test\'', user.id).from(user).where(user.name.like('A%')), + pg: { + text : 'INSERT INTO "post" ("content", "userId") SELECT \'test\', "user"."id" FROM "user" WHERE ("user"."name" LIKE $1)', + string: 'INSERT INTO "post" ("content", "userId") SELECT \'test\', "user"."id" FROM "user" WHERE ("user"."name" LIKE \'A%\')' + }, + sqlite: { + text : 'INSERT INTO "post" ("content", "userId") SELECT \'test\', "user"."id" FROM "user" WHERE ("user"."name" LIKE $1)', + string: 'INSERT INTO "post" ("content", "userId") SELECT \'test\', "user"."id" FROM "user" WHERE ("user"."name" LIKE \'A%\')' + }, + mysql: { + text : 'INSERT INTO `post` (`content`, `userId`) SELECT \'test\', `user`.`id` FROM `user` WHERE (`user`.`name` LIKE ?)', + string: 'INSERT INTO `post` (`content`, `userId`) SELECT \'test\', `user`.`id` FROM `user` WHERE (`user`.`name` LIKE \'A%\')' + }, + mssql: { + text : 'INSERT INTO [post] ([content], [userId]) SELECT \'test\', [user].[id] FROM [user] WHERE ([user].[name] LIKE @1)', + string: 'INSERT INTO [post] ([content], [userId]) SELECT \'test\', [user].[id] FROM [user] WHERE ([user].[name] LIKE \'A%\')' + }, + oracle: { + text : 'INSERT INTO "post" ("content", "userId") SELECT \'test\', "user"."id" FROM "user" WHERE ("user"."name" LIKE :1)', + string: 'INSERT INTO "post" ("content", "userId") SELECT \'test\', "user"."id" FROM "user" WHERE ("user"."name" LIKE \'A%\')' + }, + params: ['A%'] +}); + +Harness.test({ + query: post.insert(post.userId) + .select(user.id).from(user).where(user.name.like('A%')), + pg: { + text : 'INSERT INTO "post" ("userId") SELECT "user"."id" FROM "user" WHERE ("user"."name" LIKE $1)', + string: 'INSERT INTO "post" ("userId") SELECT "user"."id" FROM "user" WHERE ("user"."name" LIKE \'A%\')' + }, + sqlite: { + text : 'INSERT INTO "post" ("userId") SELECT "user"."id" FROM "user" WHERE ("user"."name" LIKE $1)', + string: 'INSERT INTO "post" ("userId") SELECT "user"."id" FROM "user" WHERE ("user"."name" LIKE \'A%\')' + }, + mysql: { + text : 'INSERT INTO `post` (`userId`) SELECT `user`.`id` FROM `user` WHERE (`user`.`name` LIKE ?)', + string: 'INSERT INTO `post` (`userId`) SELECT `user`.`id` FROM `user` WHERE (`user`.`name` LIKE \'A%\')' + }, + oracle: { + text : 'INSERT INTO "post" ("userId") SELECT "user"."id" FROM "user" WHERE ("user"."name" LIKE :1)', + string: 'INSERT INTO "post" ("userId") SELECT "user"."id" FROM "user" WHERE ("user"."name" LIKE \'A%\')' + }, + params: ['A%'] +}); + +Harness.test({ + query: post.insert(post.userId) + .select(post.userId).from(user.join(post).on(user.id.equals(post.userId))).where(post.tags.like('A%')), + pg: { + text : 'INSERT INTO "post" ("userId") SELECT "post"."userId" FROM "user" INNER JOIN "post" ON ("user"."id" = "post"."userId") WHERE ("post"."tags" LIKE $1)', + string: 'INSERT INTO "post" ("userId") SELECT "post"."userId" FROM "user" INNER JOIN "post" ON ("user"."id" = "post"."userId") WHERE ("post"."tags" LIKE \'A%\')' + }, + sqlite: { + text : 'INSERT INTO "post" ("userId") SELECT "post"."userId" FROM "user" INNER JOIN "post" ON ("user"."id" = "post"."userId") WHERE ("post"."tags" LIKE $1)', + string: 'INSERT INTO "post" ("userId") SELECT "post"."userId" FROM "user" INNER JOIN "post" ON ("user"."id" = "post"."userId") WHERE ("post"."tags" LIKE \'A%\')' + }, + mysql: { + text : 'INSERT INTO `post` (`userId`) SELECT `post`.`userId` FROM `user` INNER JOIN `post` ON (`user`.`id` = `post`.`userId`) WHERE (`post`.`tags` LIKE ?)', + string: 'INSERT INTO `post` (`userId`) SELECT `post`.`userId` FROM `user` INNER JOIN `post` ON (`user`.`id` = `post`.`userId`) WHERE (`post`.`tags` LIKE \'A%\')' + }, + mssql: { + text : 'INSERT INTO [post] ([userId]) SELECT [post].[userId] FROM [user] INNER JOIN [post] ON ([user].[id] = [post].[userId]) WHERE ([post].[tags] LIKE @1)', + string: 'INSERT INTO [post] ([userId]) SELECT [post].[userId] FROM [user] INNER JOIN [post] ON ([user].[id] = [post].[userId]) WHERE ([post].[tags] LIKE \'A%\')' + }, + oracle: { + text : 'INSERT INTO "post" ("userId") SELECT "post"."userId" FROM "user" INNER JOIN "post" ON ("user"."id" = "post"."userId") WHERE ("post"."tags" LIKE :1)', + string: 'INSERT INTO "post" ("userId") SELECT "post"."userId" FROM "user" INNER JOIN "post" ON ("user"."id" = "post"."userId") WHERE ("post"."tags" LIKE \'A%\')' + }, + params: ['A%'] +}); + +Harness.test({ + query: post.insert(post.userId).select(user.id).distinct().from(user), + pg: { + text : 'INSERT INTO "post" ("userId") SELECT DISTINCT "user"."id" FROM "user"', + string: 'INSERT INTO "post" ("userId") SELECT DISTINCT "user"."id" FROM "user"' + }, + sqlite: { + text : 'INSERT INTO "post" ("userId") SELECT DISTINCT "user"."id" FROM "user"', + string: 'INSERT INTO "post" ("userId") SELECT DISTINCT "user"."id" FROM "user"' + }, + mysql: { + text : 'INSERT INTO `post` (`userId`) SELECT DISTINCT `user`.`id` FROM `user`', + string: 'INSERT INTO `post` (`userId`) SELECT DISTINCT `user`.`id` FROM `user`' + }, + mssql: { + text : 'INSERT INTO [post] ([userId]) SELECT DISTINCT [user].[id] FROM [user]', + string: 'INSERT INTO [post] ([userId]) SELECT DISTINCT [user].[id] FROM [user]' + }, + oracle: { + text : 'INSERT INTO "post" ("userId") SELECT DISTINCT "user"."id" FROM "user"', + string: 'INSERT INTO "post" ("userId") SELECT DISTINCT "user"."id" FROM "user"' + }, + params: [] +}); + +// Binary inserts +Harness.test({ + query: post.insert(post.content.value(new Buffer('test')), post.userId.value(2)), + pg: { + text : 'INSERT INTO "post" ("content", "userId") VALUES ($1, $2)', + string: 'INSERT INTO "post" ("content", "userId") VALUES (\'\\x74657374\', 2)' + }, + sqlite: { + text : 'INSERT INTO "post" ("content", "userId") VALUES ($1, $2)', + string: 'INSERT INTO "post" ("content", "userId") VALUES (x\'74657374\', 2)' + }, + mysql: { + text : 'INSERT INTO `post` (`content`, `userId`) VALUES (?, ?)', + string: 'INSERT INTO `post` (`content`, `userId`) VALUES (x\'74657374\', 2)' + }, + mssql: { + text : 'INSERT INTO [post] ([content], [userId]) VALUES (@1, @2)', + string: 'INSERT INTO [post] ([content], [userId]) VALUES (\'\\x74657374\', 2)' + }, + oracle: { + text : 'INSERT INTO "post" ("content", "userId") VALUES (:1, :2)', + string: 'INSERT INTO "post" ("content", "userId") VALUES (utl_raw.cast_to_varchar2(hextoraw(\'74657374\')), 2)' + }, + params: [new Buffer('test'), 2] +}); + +Harness.test({ + query: post.insert({ + content: new Buffer('test'), + userId: 2 + }), + pg: { + text : 'INSERT INTO "post" ("content", "userId") VALUES ($1, $2)', + string: 'INSERT INTO "post" ("content", "userId") VALUES (\'\\x74657374\', 2)' + }, + sqlite: { + text : 'INSERT INTO "post" ("content", "userId") VALUES ($1, $2)', + string: 'INSERT INTO "post" ("content", "userId") VALUES (x\'74657374\', 2)' + }, + mysql: { + text : 'INSERT INTO `post` (`content`, `userId`) VALUES (?, ?)', + string: 'INSERT INTO `post` (`content`, `userId`) VALUES (x\'74657374\', 2)' + }, + mssql: { + text : 'INSERT INTO [post] ([content], [userId]) VALUES (@1, @2)', + string: 'INSERT INTO [post] ([content], [userId]) VALUES (\'\\x74657374\', 2)' + }, + oracle: { + text : 'INSERT INTO "post" ("content", "userId") VALUES (:1, :2)', + string: 'INSERT INTO "post" ("content", "userId") VALUES (utl_raw.cast_to_varchar2(hextoraw(\'74657374\')), 2)' + }, + params: [new Buffer('test'), 2] +}); + +Harness.test({ + query: post.insert([{ + content: new Buffer('whoah') + }, { + content: new Buffer('hey') + } + ]), + pg: { + text : 'INSERT INTO "post" ("content") VALUES ($1), ($2)', + string: 'INSERT INTO "post" ("content") ' + + 'VALUES (\'\\x77686f6168\'), (\'\\x686579\')' + }, + sqlite: { + text : 'INSERT INTO "post" ("content") VALUES ($1), ($2)', + string: 'INSERT INTO "post" ("content") VALUES (x\'77686f6168\'), (x\'686579\')' + }, + mysql: { + text : 'INSERT INTO `post` (`content`) VALUES (?), (?)', + string: 'INSERT INTO `post` (`content`) VALUES (x\'77686f6168\'), (x\'686579\')' + }, + mssql: { + text : 'INSERT INTO [post] ([content]) VALUES (@1), (@2)', + string: 'INSERT INTO [post] ([content]) VALUES (\'\\x77686f6168\'), (\'\\x686579\')' + }, + oracle: { + text : 'INSERT INTO "post" ("content") VALUES (:1), (:2)', + string: 'INSERT INTO "post" ("content") VALUES (utl_raw.cast_to_varchar2(hextoraw(\'77686f6168\'))), (utl_raw.cast_to_varchar2(hextoraw(\'686579\')))' + }, + params: [new Buffer('whoah'), new Buffer('hey')] +}); + +Harness.test({ + query: post.insert({ + content: 'test', + userId: 2 + }).onDuplicate({ + content: 'testupdate', + }), + pg: { + throws: true + }, + sqlite: { + throws: true + }, + mysql: { + text : 'INSERT INTO `post` (`content`, `userId`) VALUES (?, ?) ON DUPLICATE KEY UPDATE `post`.`content` = ?', + string: 'INSERT INTO `post` (`content`, `userId`) VALUES (\'test\', 2) ON DUPLICATE KEY UPDATE `post`.`content` = \'testupdate\'' + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: ['test', 2, 'testupdate'] +}); + +Harness.test({ + query: customerAliasTable.insert({ + id : 2, + name : 'test' + }).onConflict({ + columns: ['id'], + update: ['name'] + }), + mysql: { + throws: true + }, + sqlite: { + throws: true + }, + pg: { + text : 'INSERT INTO "customer" ("id", "name") VALUES ($1, $2) ON CONFLICT ("id") DO UPDATE SET "name" = EXCLUDED."name"', + string: 'INSERT INTO "customer" ("id", "name") VALUES (2, \'test\') ON CONFLICT ("id") DO UPDATE SET "name" = EXCLUDED."name"' + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: [2, 'test'] +}); + +Harness.test({ + query: customerAliasTable.insert({ + id : 2, + name : 'test' + }).orIgnore(), + mysql: { + throws: true + }, + sqlite: { + text : 'INSERT OR IGNORE INTO "customer" ("id", "name") VALUES ($1, $2)', + string: 'INSERT OR IGNORE INTO "customer" ("id", "name") VALUES (2, \'test\')' + }, + pg: { + throws: true + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: [2, 'test'] +}); + +Harness.test({ + query: post.insert({ + content: 'test', + userId: 2 + }).onConflict({ + columns: ['userId'], + update: ['content'] + }), + mysql: { + throws: true + }, + sqlite: { + throws: true + }, + pg: { + text : 'INSERT INTO "post" ("content", "userId") VALUES ($1, $2) ON CONFLICT ("userId") DO UPDATE SET "content" = EXCLUDED."content"', + string: 'INSERT INTO "post" ("content", "userId") VALUES (\'test\', 2) ON CONFLICT ("userId") DO UPDATE SET "content" = EXCLUDED."content"' + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: ['test', 2] +}); + +Harness.test({ + query: post.insert({ + content: 'test', + userId: 2 + }).onConflict({ + columns: ['userId','content'], + update: ['content','userId'] + }), + mysql: { + throws: true + }, + sqlite: { + throws: true + }, + pg: { + text : 'INSERT INTO "post" ("content", "userId") VALUES ($1, $2) ON CONFLICT ("userId", "content") DO UPDATE SET "content" = EXCLUDED."content", "userId" = EXCLUDED."userId"', + string: 'INSERT INTO "post" ("content", "userId") VALUES (\'test\', 2) ON CONFLICT ("userId", "content") DO UPDATE SET "content" = EXCLUDED."content", "userId" = EXCLUDED."userId"' + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: ['test', 2] +}); + +Harness.test({ + query: post.insert({ + content: 'test', + userId: 2 + }).onConflict({ + columns: ['userId'], + update: ['content'] + }).where(post.userId.equals(2)), + mysql: { + throws: true + }, + sqlite: { + throws: true + }, + pg: { + text : 'INSERT INTO "post" ("content", "userId") VALUES ($1, $2) ON CONFLICT ("userId") DO UPDATE SET "content" = EXCLUDED."content" WHERE ("post"."userId" = $3)', + string: 'INSERT INTO "post" ("content", "userId") VALUES (\'test\', 2) ON CONFLICT ("userId") DO UPDATE SET "content" = EXCLUDED."content" WHERE ("post"."userId" = 2)' + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: ['test', 2, 2] +}); + +Harness.test({ + query: post.insert({ + content: 'test', + userId: 2 + }).onConflict({ + constraint: 'conc_userId', + update: ['content'] + }).where(post.userId.equals(2)), + mysql: { + throws: true + }, + sqlite: { + throws: true + }, + pg: { + text : 'INSERT INTO "post" ("content", "userId") VALUES ($1, $2) ON CONFLICT ON CONSTRAINT "conc_userId" DO UPDATE SET "content" = EXCLUDED."content" WHERE ("post"."userId" = $3)', + string: 'INSERT INTO "post" ("content", "userId") VALUES (\'test\', 2) ON CONFLICT ON CONSTRAINT "conc_userId" DO UPDATE SET "content" = EXCLUDED."content" WHERE ("post"."userId" = 2)' + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: ['test', 2, 2] +}); + +Harness.test({ + query: post.insert({ + content: 'test', + userId: 2 + }).onConflict({ + columns: ['userId'], + }), + mysql: { + throws: true + }, + sqlite: { + throws: true + }, + pg: { + text : 'INSERT INTO "post" ("content", "userId") VALUES ($1, $2) ON CONFLICT ("userId") DO NOTHING', + string: 'INSERT INTO "post" ("content", "userId") VALUES (\'test\', 2) ON CONFLICT ("userId") DO NOTHING' + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: ['test', 2] +}); + +Harness.test({ + query: post.insert({ + content: 'test', + userId: 2 + }).onConflict({ + constraint: 'conc_userId', + }), + mysql: { + throws: true + }, + sqlite: { + throws: true + }, + pg: { + text : 'INSERT INTO "post" ("content", "userId") VALUES ($1, $2) ON CONFLICT ON CONSTRAINT "conc_userId" DO NOTHING', + string: 'INSERT INTO "post" ("content", "userId") VALUES (\'test\', 2) ON CONFLICT ON CONSTRAINT "conc_userId" DO NOTHING' + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: ['test', 2] +}); + +Harness.test({ + query: contentTable.insert({ + contentId: 20, + text : "something" + }).onConflict({ + columns: ['contentId'], + }), + mysql: { + throws: true + }, + sqlite: { + throws: true + }, + pg: { + text : 'INSERT INTO "content" ("content_id", "text") VALUES ($1, $2) ON CONFLICT ("content_id") DO NOTHING', + string: 'INSERT INTO "content" ("content_id", "text") VALUES (20, \'something\') ON CONFLICT ("content_id") DO NOTHING' + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: [20, "something"] +}); + +Harness.test({ + query: contentTable.insert({ + contentId: 20, + text : "something", + contentPosts : "another thing", + }).onConflict({ + columns: ['contentId'], + update: ['contentPosts'] + }), + mysql: { + throws: true + }, + sqlite: { + throws: true + }, + pg: { + text : 'INSERT INTO "content" ("content_id", "text", "content_posts") VALUES ($1, $2, $3) ON CONFLICT ("content_id") DO UPDATE SET "content_posts" = EXCLUDED."content_posts"', + string: 'INSERT INTO "content" ("content_id", "text", "content_posts") VALUES (20, \'something\', \'another thing\') ON CONFLICT ("content_id") DO UPDATE SET "content_posts" = EXCLUDED."content_posts"' + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: [20, "something", "another thing"] +}); + +Harness.test({ + query: post.insert([]), + + mysql: { + text : 'SELECT `post`.* FROM `post` WHERE (1=2)', + string: 'SELECT `post`.* FROM `post` WHERE (1=2)' + }, + params: [] +}); + +Harness.test({ + query: arrayTable.insert(arrayTable.id.value(1), arrayTable.numbers.value([2, 3, 4])), + pg: { + text : 'INSERT INTO "arraytest" ("id", "numbers") VALUES ($1, $2)', + string: 'INSERT INTO "arraytest" ("id", "numbers") VALUES (1, \'{2,3,4}\')' + }, + sqlite: { + text : 'INSERT INTO "arraytest" ("id", "numbers") VALUES ($1, $2)', + string: 'INSERT INTO "arraytest" ("id", "numbers") VALUES (1, \'[2,3,4]\')' + }, + mysql: { + text : 'INSERT INTO `arraytest` (`id`, `numbers`) VALUES (?, ?)', + string: 'INSERT INTO `arraytest` (`id`, `numbers`) VALUES (1, (2, 3, 4))' + }, + oracle: { + text : 'INSERT INTO "arraytest" ("id", "numbers") VALUES (:1, :2)', + string: 'INSERT INTO "arraytest" ("id", "numbers") VALUES (1, (2, 3, 4))' } }); + +Harness.test({ + query: arrayTable.insert(arrayTable.id.value(1), arrayTable.numbers.value(["one", "two", "three"])), + pg: { + text : 'INSERT INTO "arraytest" ("id", "numbers") VALUES ($1, $2)', + string: 'INSERT INTO "arraytest" ("id", "numbers") VALUES (1, \'{"one","two","three"}\')' + }, + sqlite: { + text : 'INSERT INTO "arraytest" ("id", "numbers") VALUES ($1, $2)', + string: 'INSERT INTO "arraytest" ("id", "numbers") VALUES (1, \'["one","two","three"]\')' + }, + mysql: { + text : 'INSERT INTO `arraytest` (`id`, `numbers`) VALUES (?, ?)', + string: 'INSERT INTO `arraytest` (`id`, `numbers`) VALUES (1, (\'one\', \'two\', \'three\'))' + }, + oracle: { + text : 'INSERT INTO "arraytest" ("id", "numbers") VALUES (:1, :2)', + string: 'INSERT INTO "arraytest" ("id", "numbers") VALUES (1, (\'one\', \'two\', \'three\'))' + } +}); + +Harness.test({ + query: post.insert(post.userId).select(user.id).from(user), + pg: { + text : 'INSERT INTO "post" ("userId") SELECT "user"."id" FROM "user"', + string: 'INSERT INTO "post" ("userId") SELECT "user"."id" FROM "user"' + }, + sqlite: { + text : 'INSERT INTO "post" ("userId") SELECT "user"."id" FROM "user"', + string: 'INSERT INTO "post" ("userId") SELECT "user"."id" FROM "user"' + }, + mysql: { + text : 'INSERT INTO `post` (`userId`) SELECT `user`.`id` FROM `user`', + string: 'INSERT INTO `post` (`userId`) SELECT `user`.`id` FROM `user`' + }, + mssql: { + text : 'INSERT INTO [post] ([userId]) SELECT [user].[id] FROM [user]', + string: 'INSERT INTO [post] ([userId]) SELECT [user].[id] FROM [user]' + }, + oracle: { + text : 'INSERT INTO "post" ("userId") SELECT "user"."id" FROM "user"', + string: 'INSERT INTO "post" ("userId") SELECT "user"."id" FROM "user"' + }, + params: [] +}); + +Harness.test({ + query: post.insert(post.userId).select(user.id).from(user).onConflict({ + columns: ['userId'], + update: ['content'] + }), + pg: { + text : 'INSERT INTO "post" ("userId") SELECT "user"."id" FROM "user" ON CONFLICT ("userId") DO UPDATE SET "content" = EXCLUDED."content"', + string: 'INSERT INTO "post" ("userId") SELECT "user"."id" FROM "user" ON CONFLICT ("userId") DO UPDATE SET "content" = EXCLUDED."content"' + }, + sqlite: { + throws: true + }, + mysql: { + throws: true + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: [] +}); + +Harness.test({ + query: post.insert(post.userId).add(user.select(user.id)), + pg: { + text : 'INSERT INTO "post" ("userId") SELECT "user"."id" FROM "user"', + string: 'INSERT INTO "post" ("userId") SELECT "user"."id" FROM "user"' + }, + sqlite: { + text : 'INSERT INTO "post" ("userId") SELECT "user"."id" FROM "user"', + string: 'INSERT INTO "post" ("userId") SELECT "user"."id" FROM "user"' + }, + mysql: { + text : 'INSERT INTO `post` (`userId`) SELECT `user`.`id` FROM `user`', + string: 'INSERT INTO `post` (`userId`) SELECT `user`.`id` FROM `user`' + }, + mssql: { + text : 'INSERT INTO [post] ([userId]) SELECT [user].[id] FROM [user]', + string: 'INSERT INTO [post] ([userId]) SELECT [user].[id] FROM [user]' + }, + oracle: { + text : 'INSERT INTO "post" ("userId") SELECT "user"."id" FROM "user"', + string: 'INSERT INTO "post" ("userId") SELECT "user"."id" FROM "user"' + }, + params: [] +}); + +Harness.test({ + query: post.insert(post.userId).add(user.select(user.id).from(user)), + pg: { + text : 'INSERT INTO "post" ("userId") SELECT "user"."id" FROM "user"', + string: 'INSERT INTO "post" ("userId") SELECT "user"."id" FROM "user"' + }, + sqlite: { + text : 'INSERT INTO "post" ("userId") SELECT "user"."id" FROM "user"', + string: 'INSERT INTO "post" ("userId") SELECT "user"."id" FROM "user"' + }, + mysql: { + text : 'INSERT INTO `post` (`userId`) SELECT `user`.`id` FROM `user`', + string: 'INSERT INTO `post` (`userId`) SELECT `user`.`id` FROM `user`' + }, + mssql: { + text : 'INSERT INTO [post] ([userId]) SELECT [user].[id] FROM [user]', + string: 'INSERT INTO [post] ([userId]) SELECT [user].[id] FROM [user]' + }, + oracle: { + text : 'INSERT INTO "post" ("userId") SELECT "user"."id" FROM "user"', + string: 'INSERT INTO "post" ("userId") SELECT "user"."id" FROM "user"' + }, + params: [] +}); + +Harness.test({ + query: post.insert(post.userId).add(user.select(user.id).order(user.id)), + pg: { + text : 'INSERT INTO "post" ("userId") SELECT "user"."id" FROM "user" ORDER BY "user"."id"', + string: 'INSERT INTO "post" ("userId") SELECT "user"."id" FROM "user" ORDER BY "user"."id"' + }, + sqlite: { + text : 'INSERT INTO "post" ("userId") SELECT "user"."id" FROM "user" ORDER BY "user"."id"', + string: 'INSERT INTO "post" ("userId") SELECT "user"."id" FROM "user" ORDER BY "user"."id"' + }, + mysql: { + text : 'INSERT INTO `post` (`userId`) SELECT `user`.`id` FROM `user` ORDER BY `user`.`id`', + string: 'INSERT INTO `post` (`userId`) SELECT `user`.`id` FROM `user` ORDER BY `user`.`id`' + }, + mssql: { + text : 'INSERT INTO [post] ([userId]) SELECT [user].[id] FROM [user] ORDER BY [user].[id]', + string: 'INSERT INTO [post] ([userId]) SELECT [user].[id] FROM [user] ORDER BY [user].[id]' + }, + oracle: { + text : 'INSERT INTO "post" ("userId") SELECT "user"."id" FROM "user" ORDER BY "user"."id"', + string: 'INSERT INTO "post" ("userId") SELECT "user"."id" FROM "user" ORDER BY "user"."id"' + }, + params: [] +}); + diff --git a/test/dialects/join-tests.js b/test/dialects/join-tests.js index d4e242af..2e0c6882 100644 --- a/test/dialects/join-tests.js +++ b/test/dialects/join-tests.js @@ -19,6 +19,14 @@ Harness.test({ text : 'SELECT `user`.`name`, `post`.`content` FROM `user` INNER JOIN `post` ON (`user`.`id` = `post`.`userId`)', string: 'SELECT `user`.`name`, `post`.`content` FROM `user` INNER JOIN `post` ON (`user`.`id` = `post`.`userId`)' }, + mssql: { + text : 'SELECT [user].[name], [post].[content] FROM [user] INNER JOIN [post] ON ([user].[id] = [post].[userId])', + string: 'SELECT [user].[name], [post].[content] FROM [user] INNER JOIN [post] ON ([user].[id] = [post].[userId])' + }, + oracle: { + text : 'SELECT "user"."name", "post"."content" FROM "user" INNER JOIN "post" ON ("user"."id" = "post"."userId")', + string: 'SELECT "user"."name", "post"."content" FROM "user" INNER JOIN "post" ON ("user"."id" = "post"."userId")' + }, params: [] }); @@ -36,6 +44,14 @@ Harness.test({ text : '`user` INNER JOIN `post` ON (`user`.`id` = `post`.`userId`)', string: '`user` INNER JOIN `post` ON (`user`.`id` = `post`.`userId`)' }, + mssql: { + text : '[user] INNER JOIN [post] ON ([user].[id] = [post].[userId])', + string: '[user] INNER JOIN [post] ON ([user].[id] = [post].[userId])' + }, + oracle: { + text : '"user" INNER JOIN "post" ON ("user"."id" = "post"."userId")', + string: '"user" INNER JOIN "post" ON ("user"."id" = "post"."userId")' + }, params: [] }); @@ -58,6 +74,14 @@ Harness.test({ text : 'SELECT `user`.`name`, `post`.`content`, `comment`.`text` FROM `user` INNER JOIN `post` ON (`user`.`id` = `post`.`userId`) INNER JOIN `comment` ON (`post`.`id` = `comment`.`postId`)', string: 'SELECT `user`.`name`, `post`.`content`, `comment`.`text` FROM `user` INNER JOIN `post` ON (`user`.`id` = `post`.`userId`) INNER JOIN `comment` ON (`post`.`id` = `comment`.`postId`)' }, + mssql: { + text : 'SELECT [user].[name], [post].[content], [comment].[text] FROM [user] INNER JOIN [post] ON ([user].[id] = [post].[userId]) INNER JOIN [comment] ON ([post].[id] = [comment].[postId])', + string: 'SELECT [user].[name], [post].[content], [comment].[text] FROM [user] INNER JOIN [post] ON ([user].[id] = [post].[userId]) INNER JOIN [comment] ON ([post].[id] = [comment].[postId])' + }, + oracle: { + text : 'SELECT "user"."name", "post"."content", "comment"."text" FROM "user" INNER JOIN "post" ON ("user"."id" = "post"."userId") INNER JOIN "comment" ON ("post"."id" = "comment"."postId")', + string: 'SELECT "user"."name", "post"."content", "comment"."text" FROM "user" INNER JOIN "post" ON ("user"."id" = "post"."userId") INNER JOIN "comment" ON ("post"."id" = "comment"."postId")' + }, params: [] }); @@ -75,6 +99,14 @@ Harness.test({ text : 'SELECT `user`.`name`, `post`.`content` FROM `user` LEFT JOIN `post` ON (`user`.`id` = `post`.`userId`)', string: 'SELECT `user`.`name`, `post`.`content` FROM `user` LEFT JOIN `post` ON (`user`.`id` = `post`.`userId`)' }, + mssql: { + text : 'SELECT [user].[name], [post].[content] FROM [user] LEFT JOIN [post] ON ([user].[id] = [post].[userId])', + string: 'SELECT [user].[name], [post].[content] FROM [user] LEFT JOIN [post] ON ([user].[id] = [post].[userId])' + }, + oracle: { + text : 'SELECT "user"."name", "post"."content" FROM "user" LEFT JOIN "post" ON ("user"."id" = "post"."userId")', + string: 'SELECT "user"."name", "post"."content" FROM "user" LEFT JOIN "post" ON ("user"."id" = "post"."userId")' + }, params: [] }); @@ -97,6 +129,14 @@ Harness.test({ text : 'SELECT `user`.`name`, `post`.`content` FROM `user` LEFT JOIN `post` ON (`user`.`id` = `post`.`userId`) LEFT JOIN `comment` ON (`post`.`id` = `comment`.`postId`)', string: 'SELECT `user`.`name`, `post`.`content` FROM `user` LEFT JOIN `post` ON (`user`.`id` = `post`.`userId`) LEFT JOIN `comment` ON (`post`.`id` = `comment`.`postId`)' }, + mssql: { + text : 'SELECT [user].[name], [post].[content] FROM [user] LEFT JOIN [post] ON ([user].[id] = [post].[userId]) LEFT JOIN [comment] ON ([post].[id] = [comment].[postId])', + string: 'SELECT [user].[name], [post].[content] FROM [user] LEFT JOIN [post] ON ([user].[id] = [post].[userId]) LEFT JOIN [comment] ON ([post].[id] = [comment].[postId])' + }, + oracle: { + text : 'SELECT "user"."name", "post"."content" FROM "user" LEFT JOIN "post" ON ("user"."id" = "post"."userId") LEFT JOIN "comment" ON ("post"."id" = "comment"."postId")', + string: 'SELECT "user"."name", "post"."content" FROM "user" LEFT JOIN "post" ON ("user"."id" = "post"."userId") LEFT JOIN "comment" ON ("post"."id" = "comment"."postId")' + }, params: [] }); @@ -113,16 +153,78 @@ Harness.test({ .from(user.join(subposts) .on(user.id.equals(subposts.subpostUserId))), pg: { - text : 'SELECT "user"."name", "subposts"."content" FROM "user" INNER JOIN (SELECT "post"."content", "post"."userId" AS "subpostUserId" FROM "post") subposts ON ("user"."id" = "subposts"."subpostUserId")', - string: 'SELECT "user"."name", "subposts"."content" FROM "user" INNER JOIN (SELECT "post"."content", "post"."userId" AS "subpostUserId" FROM "post") subposts ON ("user"."id" = "subposts"."subpostUserId")' + text : 'SELECT "user"."name", "subposts"."content" FROM "user" INNER JOIN (SELECT "post"."content", "post"."userId" AS "subpostUserId" FROM "post") "subposts" ON ("user"."id" = "subposts"."subpostUserId")', + string: 'SELECT "user"."name", "subposts"."content" FROM "user" INNER JOIN (SELECT "post"."content", "post"."userId" AS "subpostUserId" FROM "post") "subposts" ON ("user"."id" = "subposts"."subpostUserId")' }, sqlite: { - text : 'SELECT "user"."name", "subposts"."content" FROM "user" INNER JOIN (SELECT "post"."content", "post"."userId" AS "subpostUserId" FROM "post") subposts ON ("user"."id" = "subposts"."subpostUserId")', - string: 'SELECT "user"."name", "subposts"."content" FROM "user" INNER JOIN (SELECT "post"."content", "post"."userId" AS "subpostUserId" FROM "post") subposts ON ("user"."id" = "subposts"."subpostUserId")' + text : 'SELECT "user"."name", "subposts"."content" FROM "user" INNER JOIN (SELECT "post"."content", "post"."userId" AS "subpostUserId" FROM "post") "subposts" ON ("user"."id" = "subposts"."subpostUserId")', + string: 'SELECT "user"."name", "subposts"."content" FROM "user" INNER JOIN (SELECT "post"."content", "post"."userId" AS "subpostUserId" FROM "post") "subposts" ON ("user"."id" = "subposts"."subpostUserId")' }, mysql: { - text : 'SELECT `user`.`name`, `subposts`.`content` FROM `user` INNER JOIN (SELECT `post`.`content`, `post`.`userId` AS `subpostUserId` FROM `post`) subposts ON (`user`.`id` = `subposts`.`subpostUserId`)', - string: 'SELECT `user`.`name`, `subposts`.`content` FROM `user` INNER JOIN (SELECT `post`.`content`, `post`.`userId` AS `subpostUserId` FROM `post`) subposts ON (`user`.`id` = `subposts`.`subpostUserId`)' + text : 'SELECT `user`.`name`, `subposts`.`content` FROM `user` INNER JOIN (SELECT `post`.`content`, `post`.`userId` AS `subpostUserId` FROM `post`) `subposts` ON (`user`.`id` = `subposts`.`subpostUserId`)', + string: 'SELECT `user`.`name`, `subposts`.`content` FROM `user` INNER JOIN (SELECT `post`.`content`, `post`.`userId` AS `subpostUserId` FROM `post`) `subposts` ON (`user`.`id` = `subposts`.`subpostUserId`)' + }, + mssql: { + text : 'SELECT [user].[name], [subposts].[content] FROM [user] INNER JOIN (SELECT [post].[content], [post].[userId] AS [subpostUserId] FROM [post]) [subposts] ON ([user].[id] = [subposts].[subpostUserId])', + string: 'SELECT [user].[name], [subposts].[content] FROM [user] INNER JOIN (SELECT [post].[content], [post].[userId] AS [subpostUserId] FROM [post]) [subposts] ON ([user].[id] = [subposts].[subpostUserId])' + }, + oracle: { + text : 'SELECT "user"."name", "subposts"."content" FROM "user" INNER JOIN (SELECT "post"."content", "post"."userId" "subpostUserId" FROM "post") "subposts" ON ("user"."id" = "subposts"."subpostUserId")', + string: 'SELECT "user"."name", "subposts"."content" FROM "user" INNER JOIN (SELECT "post"."content", "post"."userId" "subpostUserId" FROM "post") "subposts" ON ("user"."id" = "subposts"."subpostUserId")' }, params: [] }); + +Harness.test({ + query: user.select().from(user.leftJoinLateral(post.subQuery().select(post.userId))), + pg: { + text : 'SELECT "user".* FROM "user" LEFT JOIN LATERAL (SELECT "post"."userId" FROM "post") ON true', + string: 'SELECT "user".* FROM "user" LEFT JOIN LATERAL (SELECT "post"."userId" FROM "post") ON true' + }, + mssql: { + text : 'SELECT [user].* FROM [user] OUTER APPLY (SELECT [post].[userId] FROM [post])', + string: 'SELECT [user].* FROM [user] OUTER APPLY (SELECT [post].[userId] FROM [post])' + }, + oracle: { + text : 'SELECT "user".* FROM "user" OUTER APPLY (SELECT "post"."userId" FROM "post")', + string: 'SELECT "user".* FROM "user" OUTER APPLY (SELECT "post"."userId" FROM "post")' + }, + params: [] +}); + +Harness.test({ + query: user.select().from(user.leftJoinLateral(post.subQuery().select(post.userId).where(user.id.equals(post.userId)))), + pg: { + text : 'SELECT "user".* FROM "user" LEFT JOIN LATERAL (SELECT "post"."userId" FROM "post" WHERE ("user"."id" = "post"."userId")) ON true', + string: 'SELECT "user".* FROM "user" LEFT JOIN LATERAL (SELECT "post"."userId" FROM "post" WHERE ("user"."id" = "post"."userId")) ON true' + }, + mssql: { + text : 'SELECT [user].* FROM [user] OUTER APPLY (SELECT [post].[userId] FROM [post] WHERE ([user].[id] = [post].[userId]))', + string: 'SELECT [user].* FROM [user] OUTER APPLY (SELECT [post].[userId] FROM [post] WHERE ([user].[id] = [post].[userId]))' + }, + oracle: { + text : 'SELECT "user".* FROM "user" OUTER APPLY (SELECT "post"."userId" FROM "post" WHERE ("user"."id" = "post"."userId"))', + string: 'SELECT "user".* FROM "user" OUTER APPLY (SELECT "post"."userId" FROM "post" WHERE ("user"."id" = "post"."userId"))' + }, + params: [] +}); + +Harness.test({ + query: user.select().from(user + .leftJoinLateral(post.subQuery().select(post.userId)) + .leftJoinLateral(comment.subQuery().select(comment.postId))), + pg: { + text : 'SELECT "user".* FROM "user" LEFT JOIN LATERAL (SELECT "post"."userId" FROM "post") ON true LEFT JOIN LATERAL (SELECT "comment"."postId" FROM "comment") ON true', + string: 'SELECT "user".* FROM "user" LEFT JOIN LATERAL (SELECT "post"."userId" FROM "post") ON true LEFT JOIN LATERAL (SELECT "comment"."postId" FROM "comment") ON true' + }, + mssql: { + text : 'SELECT [user].* FROM [user] OUTER APPLY (SELECT [post].[userId] FROM [post]) OUTER APPLY (SELECT [comment].[postId] FROM [comment])', + string: 'SELECT [user].* FROM [user] OUTER APPLY (SELECT [post].[userId] FROM [post]) OUTER APPLY (SELECT [comment].[postId] FROM [comment])' + }, + oracle: { + text : 'SELECT "user".* FROM "user" OUTER APPLY (SELECT "post"."userId" FROM "post") OUTER APPLY (SELECT "comment"."postId" FROM "comment")', + string: 'SELECT "user".* FROM "user" OUTER APPLY (SELECT "post"."userId" FROM "post") OUTER APPLY (SELECT "comment"."postId" FROM "comment")' + }, + params: [] +}); + diff --git a/test/dialects/join-to-tests.js b/test/dialects/join-to-tests.js index 5aca7e39..d50f8817 100644 --- a/test/dialects/join-to-tests.js +++ b/test/dialects/join-to-tests.js @@ -50,6 +50,14 @@ Harness.test({ text : '`user` INNER JOIN `post` ON (`user`.`id` = `post`.`ownerId`)', string: '`user` INNER JOIN `post` ON (`user`.`id` = `post`.`ownerId`)' }, + mssql: { + text : '[user] INNER JOIN [post] ON ([user].[id] = [post].[ownerId])', + string: '[user] INNER JOIN [post] ON ([user].[id] = [post].[ownerId])' + }, + oracle: { + text : '"user" INNER JOIN "post" ON ("user"."id" = "post"."ownerId")', + string: '"user" INNER JOIN "post" ON ("user"."id" = "post"."ownerId")' + }, params: [] }); @@ -67,6 +75,14 @@ Harness.test({ text : '`post` INNER JOIN `user` ON (`user`.`id` = `post`.`ownerId`)', string: '`post` INNER JOIN `user` ON (`user`.`id` = `post`.`ownerId`)' }, + mssql: { + text : '[post] INNER JOIN [user] ON ([user].[id] = [post].[ownerId])', + string: '[post] INNER JOIN [user] ON ([user].[id] = [post].[ownerId])' + }, + oracle: { + text : '"post" INNER JOIN "user" ON ("user"."id" = "post"."ownerId")', + string: '"post" INNER JOIN "user" ON ("user"."id" = "post"."ownerId")' + }, params: [] }); @@ -84,5 +100,13 @@ Harness.test({ text : '`user` INNER JOIN `photo` ON (`user`.`id` = `photo`.`ownerId`)', string: '`user` INNER JOIN `photo` ON (`user`.`id` = `photo`.`ownerId`)' }, + mssql: { + text : '[user] INNER JOIN [photo] ON ([user].[id] = [photo].[ownerId])', + string: '[user] INNER JOIN [photo] ON ([user].[id] = [photo].[ownerId])' + }, + oracle: { + text : '"user" INNER JOIN "photo" ON ("user"."id" = "photo"."ownerId")', + string: '"user" INNER JOIN "photo" ON ("user"."id" = "photo"."ownerId")' + }, params: [] }); diff --git a/test/dialects/json-tests.js b/test/dialects/json-tests.js new file mode 100644 index 00000000..b610b96f --- /dev/null +++ b/test/dialects/json-tests.js @@ -0,0 +1,41 @@ +'use strict'; + +var Harness = require('./support'); +var customer = Harness.defineCustomerTable(); +var Sql = require('../../lib').setDialect('postgres'); + +Harness.test({ + query: customer.select(customer.metadata.key('age')), + pg: { + text : 'SELECT ("customer"."metadata" -> $1) FROM "customer"', + string: 'SELECT ("customer"."metadata" -> \'age\') FROM "customer"' + }, + params: ['age'] +}); + +Harness.test({ + query: customer.select(customer.metadata.keyText('age')), + pg: { + text : 'SELECT ("customer"."metadata" ->> $1) FROM "customer"', + string: 'SELECT ("customer"."metadata" ->> \'age\') FROM "customer"' + }, + params: ['age'] +}); + +Harness.test({ + query: customer.select(customer.metadata.path('{address,city}')), + pg: { + text : 'SELECT ("customer"."metadata" #> $1) FROM "customer"', + string: 'SELECT ("customer"."metadata" #> \'{address,city}\') FROM "customer"' + }, + params: ['{address,city}'] +}); + +Harness.test({ + query: customer.select(customer.metadata.pathText('{address,city}')), + pg: { + text : 'SELECT ("customer"."metadata" #>> $1) FROM "customer"', + string: 'SELECT ("customer"."metadata" #>> \'{address,city}\') FROM "customer"' + }, + params: ['{address,city}'] +}); diff --git a/test/dialects/limit-and-offset-tests.js b/test/dialects/limit-and-offset-tests.js index aa83033c..6561c14b 100644 --- a/test/dialects/limit-and-offset-tests.js +++ b/test/dialects/limit-and-offset-tests.js @@ -20,6 +20,10 @@ Harness.test({ text : 'SELECT `user`.* FROM `user` ORDER BY `user`.`name` LIMIT 1', string: 'SELECT `user`.* FROM `user` ORDER BY `user`.`name` LIMIT 1' }, + mssql: { + text : 'SELECT TOP(1) [user].* FROM [user] ORDER BY [user].[name]', + string: 'SELECT TOP(1) [user].* FROM [user] ORDER BY [user].[name]' + }, params: [] }); @@ -37,6 +41,10 @@ Harness.test({ text : 'SELECT `user`.* FROM `user` ORDER BY `user`.`name` LIMIT 3 OFFSET 6', string: 'SELECT `user`.* FROM `user` ORDER BY `user`.`name` LIMIT 3 OFFSET 6' }, + mssql: { + text : 'SELECT [user].* FROM [user] ORDER BY [user].[name] OFFSET 6 ROWS FETCH NEXT 3 ROWS ONLY', + string: 'SELECT [user].* FROM [user] ORDER BY [user].[name] OFFSET 6 ROWS FETCH NEXT 3 ROWS ONLY' + }, params: [] }); @@ -54,6 +62,14 @@ Harness.test({ text : 'SELECT `user`.* FROM `user` ORDER BY `user`.`name` OFFSET 10', string: 'SELECT `user`.* FROM `user` ORDER BY `user`.`name` OFFSET 10' }, + mssql: { + text : 'SELECT [user].* FROM [user] ORDER BY [user].[name] OFFSET 10 ROWS', + string: 'SELECT [user].* FROM [user] ORDER BY [user].[name] OFFSET 10 ROWS' + }, + oracle: { + text : 'SELECT "user".* FROM "user" ORDER BY "user"."name" OFFSET 10 ROWS', + string: 'SELECT "user".* FROM "user" ORDER BY "user"."name" OFFSET 10 ROWS' + }, params: [] }); @@ -75,5 +91,15 @@ Harness.test({ text : 'SELECT `user`.* FROM `user` WHERE (`user`.`name` = ?) OFFSET (SELECT FLOOR(RANDOM() * COUNT(*)) FROM `user` WHERE (`user`.`name` = ?)) LIMIT 1', string: 'SELECT `user`.* FROM `user` WHERE (`user`.`name` = \'John\') OFFSET (SELECT FLOOR(RANDOM() * COUNT(*)) FROM `user` WHERE (`user`.`name` = \'John\')) LIMIT 1' }, + mssql: { + text : 'Microsoft SQL Server does not support OFFSET without and ORDER BY.', + throws: true + }, + oracle: { + text : 'SELECT "user".* FROM "user" WHERE ("user"."name" = :1) OFFSET (SELECT FLOOR(RANDOM() * COUNT(*)) FROM "user" WHERE ("user"."name" = :2)) ROWS FETCH NEXT 1 ROWS ONLY', + string: 'SELECT "user".* FROM "user" WHERE ("user"."name" = \'John\') OFFSET (SELECT FLOOR(RANDOM() * COUNT(*)) FROM "user" WHERE ("user"."name" = \'John\')) ROWS FETCH NEXT 1 ROWS ONLY' + }, values: ['John', 'John'] }); + +// TODO: Should probably have a test case like the one above but including an ORDER BY clause so the mssql case can be tested \ No newline at end of file diff --git a/test/dialects/literal-tests.js b/test/dialects/literal-tests.js new file mode 100644 index 00000000..c31d4271 --- /dev/null +++ b/test/dialects/literal-tests.js @@ -0,0 +1,72 @@ +'use strict'; + +var Harness = require('./support'); +var user = Harness.defineUserTable(); + +Harness.test({ + query: user.select(user.literal('foo'), user.name, user.literal('123').as('onetwothree')), + pg: { + text : 'SELECT foo, "user"."name", 123 AS "onetwothree" FROM "user"', + string: 'SELECT foo, "user"."name", 123 AS "onetwothree" FROM "user"' + }, + sqlite: { + text : 'SELECT foo, "user"."name", 123 AS "onetwothree" FROM "user"', + string: 'SELECT foo, "user"."name", 123 AS "onetwothree" FROM "user"' + }, + mysql: { + text : 'SELECT foo, `user`.`name`, 123 AS `onetwothree` FROM `user`', + string: 'SELECT foo, `user`.`name`, 123 AS `onetwothree` FROM `user`' + }, + oracle: { + text : 'SELECT foo, "user"."name", 123 "onetwothree" FROM "user"', + string: 'SELECT foo, "user"."name", 123 "onetwothree" FROM "user"' + }, + params: [] +}); + + +Harness.test({ + query: user.select().where(user.literal('foo = bar')), + pg: { + text : 'SELECT "user".* FROM "user" WHERE foo = bar', + string: 'SELECT "user".* FROM "user" WHERE foo = bar' + }, + sqlite: { + text : 'SELECT "user".* FROM "user" WHERE foo = bar', + string: 'SELECT "user".* FROM "user" WHERE foo = bar' + }, + mysql: { + text : 'SELECT `user`.* FROM `user` WHERE foo = bar', + string: 'SELECT `user`.* FROM `user` WHERE foo = bar' + }, + oracle: { + text : 'SELECT "user".* FROM "user" WHERE foo = bar', + string: 'SELECT "user".* FROM "user" WHERE foo = bar' + }, + params: [] +}); + +// A real world example: "How many records does page 3 have?" +// This could be less than 10 (the limit) if we are on the last page. +var subquery = user.subQuery('subquery_for_count').select(user.literal(1).as('count_column')).limit(10).offset(20); + +Harness.test({ + query: user.select(subquery.count_column.count()).from(subquery), + pg: { + text : 'SELECT COUNT("subquery_for_count"."count_column") AS "count_column_count" FROM (SELECT 1 AS "count_column" FROM "user" LIMIT 10 OFFSET 20) "subquery_for_count"', + string: 'SELECT COUNT("subquery_for_count"."count_column") AS "count_column_count" FROM (SELECT 1 AS "count_column" FROM "user" LIMIT 10 OFFSET 20) "subquery_for_count"' + }, + sqlite: { + text : 'SELECT COUNT("subquery_for_count"."count_column") AS "count_column_count" FROM (SELECT 1 AS "count_column" FROM "user" LIMIT 10 OFFSET 20) "subquery_for_count"', + string: 'SELECT COUNT("subquery_for_count"."count_column") AS "count_column_count" FROM (SELECT 1 AS "count_column" FROM "user" LIMIT 10 OFFSET 20) "subquery_for_count"' + }, + mysql: { + text : 'SELECT COUNT(`subquery_for_count`.`count_column`) AS `count_column_count` FROM (SELECT 1 AS `count_column` FROM `user` LIMIT 10 OFFSET 20) `subquery_for_count`', + string: 'SELECT COUNT(`subquery_for_count`.`count_column`) AS `count_column_count` FROM (SELECT 1 AS `count_column` FROM `user` LIMIT 10 OFFSET 20) `subquery_for_count`' + }, + oracle: { + text : 'SELECT COUNT("subquery_for_count"."count_column") "count_column_count" FROM (SELECT 1 "count_column" FROM "user" OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY) "subquery_for_count"', + string: 'SELECT COUNT("subquery_for_count"."count_column") "count_column_count" FROM (SELECT 1 "count_column" FROM "user" OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY) "subquery_for_count"' + }, + params: [] +}); diff --git a/test/dialects/matches-test.js b/test/dialects/matches-test.js new file mode 100644 index 00000000..c385641c --- /dev/null +++ b/test/dialects/matches-test.js @@ -0,0 +1,49 @@ +'use strict'; + +var Harness = require('./support'); +var post = Harness.definePostTable(); +var customerAlias = Harness.defineCustomerAliasTable(); +var sql = require(__dirname + '/../../lib').setDialect('postgres'); + +//Postgres needs the to_tsquery function to use with @@ operator +Harness.test({ + query: post.select(post.star()).where(post.content.match(sql.functions.TO_TSQUERY('hello'))), + pg: { + text : 'SELECT "post".* FROM "post" WHERE ("post"."content" @@ TO_TSQUERY($1))', + string: 'SELECT "post".* FROM "post" WHERE ("post"."content" @@ TO_TSQUERY(\'hello\'))' + }, + params: ['hello'] +}); + + +Harness.test({ + query: post.select(post.star()).where(post.content.match('hello')), + sqlite: { + text : 'SELECT "post".* FROM "post" WHERE ("post"."content" MATCH $1)', + string: 'SELECT "post".* FROM "post" WHERE ("post"."content" MATCH \'hello\')' + }, + mysql: { + text : 'SELECT `post`.* FROM `post` WHERE (MATCH `post`.`content` AGAINST ?)', + string: 'SELECT `post`.* FROM `post` WHERE (MATCH `post`.`content` AGAINST \'hello\')' + }, + mssql: { + text : 'SELECT [post].* FROM [post] WHERE (CONTAINS ([post].[content], @1))', + string: 'SELECT [post].* FROM [post] WHERE (CONTAINS ([post].[content], \'hello\'))' + }, + oracle: { + text : 'SELECT "post".* FROM "post" WHERE (INSTR ("post"."content", :1) > 0)', + string: 'SELECT "post".* FROM "post" WHERE (INSTR ("post"."content", \'hello\') > 0)' + }, + params: ['hello'] +}); + +//matches, ordered by best rank first +Harness.test({ + query: post.select(post.id, sql.functions.TS_RANK_CD(post.content, sql.functions.TO_TSQUERY('hello')).as('rank')). + where(post.content.match(sql.functions.TO_TSQUERY('hello'))).order(sql.functions.TS_RANK_CD(post.content, sql.functions.TO_TSQUERY('hello')).descending()), + pg: { + text : 'SELECT "post"."id", TS_RANK_CD("post"."content", TO_TSQUERY($1)) AS "rank" FROM "post" WHERE ("post"."content" @@ TO_TSQUERY($2)) ORDER BY TS_RANK_CD("post"."content", TO_TSQUERY($3)) DESC', + string: 'SELECT "post"."id", TS_RANK_CD("post"."content", TO_TSQUERY(\'hello\')) AS "rank" FROM "post" WHERE ("post"."content" @@ TO_TSQUERY(\'hello\')) ORDER BY TS_RANK_CD("post"."content", TO_TSQUERY(\'hello\')) DESC' + }, + params: ['hello','hello','hello'] +}); diff --git a/test/dialects/namespace-tests.js b/test/dialects/namespace-tests.js index 2ebeb086..5f6b4a56 100644 --- a/test/dialects/namespace-tests.js +++ b/test/dialects/namespace-tests.js @@ -20,6 +20,14 @@ Harness.test({ text : 'SELECT `u`.`name` FROM `user` AS `u`', string: 'SELECT `u`.`name` FROM `user` AS `u`' }, + mssql: { + text : 'SELECT [u].[name] FROM [user] AS [u]', + string: 'SELECT [u].[name] FROM [user] AS [u]' + }, + oracle: { + text : 'SELECT "u"."name" FROM "user" "u"', + string: 'SELECT "u"."name" FROM "user" "u"' + }, params: [] }); @@ -37,6 +45,14 @@ Harness.test({ text : 'SELECT `u`.* FROM `user` AS `u`', string: 'SELECT `u`.* FROM `user` AS `u`' }, + mssql: { + text : 'SELECT [u].* FROM [user] AS [u]', + string: 'SELECT [u].* FROM [user] AS [u]' + }, + oracle: { + text : 'SELECT "u".* FROM "user" "u"', + string: 'SELECT "u".* FROM "user" "u"' + }, params: [] }); @@ -55,6 +71,14 @@ Harness.test({ text : 'SELECT `u`.`name` FROM `user` AS `u` INNER JOIN `post` AS `p` ON ((`u`.`id` = `p`.`userId`) AND (`p`.`id` = ?))', string: 'SELECT `u`.`name` FROM `user` AS `u` INNER JOIN `post` AS `p` ON ((`u`.`id` = `p`.`userId`) AND (`p`.`id` = 3))' }, + mssql: { + text : 'SELECT [u].[name] FROM [user] AS [u] INNER JOIN [post] AS [p] ON (([u].[id] = [p].[userId]) AND ([p].[id] = @1))', + string: 'SELECT [u].[name] FROM [user] AS [u] INNER JOIN [post] AS [p] ON (([u].[id] = [p].[userId]) AND ([p].[id] = 3))' + }, + oracle: { + text : 'SELECT "u"."name" FROM "user" "u" INNER JOIN "post" "p" ON (("u"."id" = "p"."userId") AND ("p"."id" = :1))', + string: 'SELECT "u"."name" FROM "user" "u" INNER JOIN "post" "p" ON (("u"."id" = "p"."userId") AND ("p"."id" = 3))' + }, params: [3] }); @@ -72,6 +96,14 @@ Harness.test({ text : 'SELECT `p`.`content`, `u`.`name` FROM `user` AS `u` INNER JOIN `post` AS `p` ON ((`u`.`id` = `p`.`userId`) AND (`p`.`content` IS NOT NULL))', string: 'SELECT `p`.`content`, `u`.`name` FROM `user` AS `u` INNER JOIN `post` AS `p` ON ((`u`.`id` = `p`.`userId`) AND (`p`.`content` IS NOT NULL))' }, + mssql: { + text : 'SELECT [p].[content], [u].[name] FROM [user] AS [u] INNER JOIN [post] AS [p] ON (([u].[id] = [p].[userId]) AND ([p].[content] IS NOT NULL))', + string: 'SELECT [p].[content], [u].[name] FROM [user] AS [u] INNER JOIN [post] AS [p] ON (([u].[id] = [p].[userId]) AND ([p].[content] IS NOT NULL))' + }, + oracle: { + text : 'SELECT "p"."content", "u"."name" FROM "user" "u" INNER JOIN "post" "p" ON (("u"."id" = "p"."userId") AND ("p"."content" IS NOT NULL))', + string: 'SELECT "p"."content", "u"."name" FROM "user" "u" INNER JOIN "post" "p" ON (("u"."id" = "p"."userId") AND ("p"."content" IS NOT NULL))' + }, params: [] }); @@ -102,5 +134,13 @@ Harness.test({ text : 'SELECT `comment`.`text`, `comment`.`userId` FROM `comment`', string: 'SELECT `comment`.`text`, `comment`.`userId` FROM `comment`' }, + mssql: { + text : 'SELECT [comment].[text], [comment].[userId] FROM [comment]', + string: 'SELECT [comment].[text], [comment].[userId] FROM [comment]' + }, + orcle: { + text : 'SELECT "comment"."text", "comment"."userId" FROM "comment"', + string: 'SELECT "comment"."text", "comment"."userId" FROM "comment"' + }, params: [] }); diff --git a/test/dialects/not-in-clause-tests.js b/test/dialects/not-in-clause-tests.js new file mode 100644 index 00000000..11647fee --- /dev/null +++ b/test/dialects/not-in-clause-tests.js @@ -0,0 +1,179 @@ +'use strict'; + +var Harness = require('./support'); +var post = Harness.definePostTable(); + +Harness.test({ + query: post.select(post.star()).where(post.id.notIn([])), + pg: { + text : 'SELECT "post".* FROM "post" WHERE (1=1)', + string: 'SELECT "post".* FROM "post" WHERE (1=1)' + }, + sqlite: { + text : 'SELECT "post".* FROM "post" WHERE (1=1)', + string: 'SELECT "post".* FROM "post" WHERE (1=1)' + }, + mysql: { + text : 'SELECT `post`.* FROM `post` WHERE (1=1)', + string: 'SELECT `post`.* FROM `post` WHERE (1=1)' + }, + mssql: { + text : 'SELECT [post].* FROM [post] WHERE (1=1)', + string: 'SELECT [post].* FROM [post] WHERE (1=1)' + }, + oracle: { + text : 'SELECT "post".* FROM "post" WHERE (1=1)', + string: 'SELECT "post".* FROM "post" WHERE (1=1)' + }, + params: [] +}); + +Harness.test({ + query: post.select(post.star()).where(post.id.notIn([1])), + pg: { + text : 'SELECT "post".* FROM "post" WHERE ("post"."id" NOT IN ($1))', + string: 'SELECT "post".* FROM "post" WHERE ("post"."id" NOT IN (1))' + }, + sqlite: { + text : 'SELECT "post".* FROM "post" WHERE ("post"."id" NOT IN ($1))', + string: 'SELECT "post".* FROM "post" WHERE ("post"."id" NOT IN (1))' + }, + mysql: { + text : 'SELECT `post`.* FROM `post` WHERE (`post`.`id` NOT IN (?))', + string: 'SELECT `post`.* FROM `post` WHERE (`post`.`id` NOT IN (1))' + }, + mssql: { + text : 'SELECT [post].* FROM [post] WHERE ([post].[id] NOT IN (@1))', + string: 'SELECT [post].* FROM [post] WHERE ([post].[id] NOT IN (1))' + }, + oracle: { + text : 'SELECT "post".* FROM "post" WHERE ("post"."id" NOT IN (:1))', + string: 'SELECT "post".* FROM "post" WHERE ("post"."id" NOT IN (1))' + }, + params: [1] +}); + +Harness.test({ + query: post.select(post.star()).where(post.id.notIn([null])), + pg: { + text : 'SELECT "post".* FROM "post" WHERE ("post"."id" IS NOT NULL)', + string: 'SELECT "post".* FROM "post" WHERE ("post"."id" IS NOT NULL)' + }, + sqlite: { + text : 'SELECT "post".* FROM "post" WHERE ("post"."id" IS NOT NULL)', + string: 'SELECT "post".* FROM "post" WHERE ("post"."id" IS NOT NULL)' + }, + mysql: { + text : 'SELECT `post`.* FROM `post` WHERE (`post`.`id` IS NOT NULL)', + string: 'SELECT `post`.* FROM `post` WHERE (`post`.`id` IS NOT NULL)' + }, + mssql: { + text : 'SELECT [post].* FROM [post] WHERE ([post].[id] IS NOT NULL)', + string: 'SELECT [post].* FROM [post] WHERE ([post].[id] IS NOT NULL)' + }, + oracle: { + text : 'SELECT "post".* FROM "post" WHERE ("post"."id" IS NOT NULL)', + string: 'SELECT "post".* FROM "post" WHERE ("post"."id" IS NOT NULL)' + }, + params: [] +}); + +Harness.test({ + query: post.select(post.star()).where(post.id.notIn([1, 2])), + pg: { + text : 'SELECT "post".* FROM "post" WHERE ("post"."id" NOT IN ($1, $2))', + string: 'SELECT "post".* FROM "post" WHERE ("post"."id" NOT IN (1, 2))' + }, + sqlite: { + text : 'SELECT "post".* FROM "post" WHERE ("post"."id" NOT IN ($1, $2))', + string: 'SELECT "post".* FROM "post" WHERE ("post"."id" NOT IN (1, 2))' + }, + mysql: { + text : 'SELECT `post`.* FROM `post` WHERE (`post`.`id` NOT IN (?, ?))', + string: 'SELECT `post`.* FROM `post` WHERE (`post`.`id` NOT IN (1, 2))' + }, + mssql: { + text : 'SELECT [post].* FROM [post] WHERE ([post].[id] NOT IN (@1, @2))', + string: 'SELECT [post].* FROM [post] WHERE ([post].[id] NOT IN (1, 2))' + }, + oracle: { + text : 'SELECT "post".* FROM "post" WHERE ("post"."id" NOT IN (:1, :2))', + string: 'SELECT "post".* FROM "post" WHERE ("post"."id" NOT IN (1, 2))' + }, + params: [1, 2] +}); + +Harness.test({ + query: post.select(post.star()).where(post.id.notIn([null, null])), + pg: { + text : 'SELECT "post".* FROM "post" WHERE ("post"."id" IS NOT NULL)', + string: 'SELECT "post".* FROM "post" WHERE ("post"."id" IS NOT NULL)' + }, + sqlite: { + text : 'SELECT "post".* FROM "post" WHERE ("post"."id" IS NOT NULL)', + string: 'SELECT "post".* FROM "post" WHERE ("post"."id" IS NOT NULL)' + }, + mysql: { + text : 'SELECT `post`.* FROM `post` WHERE (`post`.`id` IS NOT NULL)', + string: 'SELECT `post`.* FROM `post` WHERE (`post`.`id` IS NOT NULL)' + }, + mssql: { + text : 'SELECT [post].* FROM [post] WHERE ([post].[id] IS NOT NULL)', + string: 'SELECT [post].* FROM [post] WHERE ([post].[id] IS NOT NULL)' + }, + oracle: { + text : 'SELECT "post".* FROM "post" WHERE ("post"."id" IS NOT NULL)', + string: 'SELECT "post".* FROM "post" WHERE ("post"."id" IS NOT NULL)' + }, + params: [] +}); + +Harness.test({ + query: post.select(post.star()).where(post.id.notIn([1, null, 2])), + pg: { + text : 'SELECT "post".* FROM "post" WHERE (NOT ("post"."id" IN ($1, $2) OR "post"."id" IS NULL))', + string: 'SELECT "post".* FROM "post" WHERE (NOT ("post"."id" IN (1, 2) OR "post"."id" IS NULL))' + }, + sqlite: { + text : 'SELECT "post".* FROM "post" WHERE (NOT ("post"."id" IN ($1, $2) OR "post"."id" IS NULL))', + string: 'SELECT "post".* FROM "post" WHERE (NOT ("post"."id" IN (1, 2) OR "post"."id" IS NULL))' + }, + mysql: { + text : 'SELECT `post`.* FROM `post` WHERE (NOT (`post`.`id` IN (?, ?) OR `post`.`id` IS NULL))', + string: 'SELECT `post`.* FROM `post` WHERE (NOT (`post`.`id` IN (1, 2) OR `post`.`id` IS NULL))' + }, + mssql: { + text : 'SELECT [post].* FROM [post] WHERE (NOT ([post].[id] IN (@1, @2) OR [post].[id] IS NULL))', + string: 'SELECT [post].* FROM [post] WHERE (NOT ([post].[id] IN (1, 2) OR [post].[id] IS NULL))' + }, + oracle: { + text : 'SELECT "post".* FROM "post" WHERE (NOT ("post"."id" IN (:1, :2) OR "post"."id" IS NULL))', + string: 'SELECT "post".* FROM "post" WHERE (NOT ("post"."id" IN (1, 2) OR "post"."id" IS NULL))' + }, + params: [1, 2] +}); + +Harness.test({ + query: post.select(post.star()).where(post.id.notIn([1, null, 2, null])), + pg: { + text : 'SELECT "post".* FROM "post" WHERE (NOT ("post"."id" IN ($1, $2) OR "post"."id" IS NULL))', + string: 'SELECT "post".* FROM "post" WHERE (NOT ("post"."id" IN (1, 2) OR "post"."id" IS NULL))' + }, + sqlite: { + text : 'SELECT "post".* FROM "post" WHERE (NOT ("post"."id" IN ($1, $2) OR "post"."id" IS NULL))', + string: 'SELECT "post".* FROM "post" WHERE (NOT ("post"."id" IN (1, 2) OR "post"."id" IS NULL))' + }, + mysql: { + text : 'SELECT `post`.* FROM `post` WHERE (NOT (`post`.`id` IN (?, ?) OR `post`.`id` IS NULL))', + string: 'SELECT `post`.* FROM `post` WHERE (NOT (`post`.`id` IN (1, 2) OR `post`.`id` IS NULL))' + }, + mssql: { + text : 'SELECT [post].* FROM [post] WHERE (NOT ([post].[id] IN (@1, @2) OR [post].[id] IS NULL))', + string: 'SELECT [post].* FROM [post] WHERE (NOT ([post].[id] IN (1, 2) OR [post].[id] IS NULL))' + }, + oracle: { + text : 'SELECT "post".* FROM "post" WHERE (NOT ("post"."id" IN (:1, :2) OR "post"."id" IS NULL))', + string: 'SELECT "post".* FROM "post" WHERE (NOT ("post"."id" IN (1, 2) OR "post"."id" IS NULL))' + }, + params: [1, 2] +}); diff --git a/test/dialects/order-tests.js b/test/dialects/order-tests.js index 64f48e1e..a0813d8e 100644 --- a/test/dialects/order-tests.js +++ b/test/dialects/order-tests.js @@ -1,7 +1,8 @@ 'use strict'; var Harness = require('./support'); -var post = Harness.definePostTable(); +var post = Harness.definePostTable(); +var sql = require('../../lib'); Harness.test({ query: post.select(post.content).order(post.content), @@ -17,6 +18,14 @@ Harness.test({ text : 'SELECT `post`.`content` FROM `post` ORDER BY `post`.`content`', string: 'SELECT `post`.`content` FROM `post` ORDER BY `post`.`content`' }, + mssql: { + text : 'SELECT [post].[content] FROM [post] ORDER BY [post].[content]', + string: 'SELECT [post].[content] FROM [post] ORDER BY [post].[content]' + }, + oracle: { + text : 'SELECT "post"."content" FROM "post" ORDER BY "post"."content"', + string: 'SELECT "post"."content" FROM "post" ORDER BY "post"."content"' + }, params: [] }); @@ -34,6 +43,14 @@ Harness.test({ text : 'SELECT `post`.`content` FROM `post` ORDER BY `post`.`content`, `post`.`userId` DESC', string: 'SELECT `post`.`content` FROM `post` ORDER BY `post`.`content`, `post`.`userId` DESC' }, + mssql: { + text : 'SELECT [post].[content] FROM [post] ORDER BY [post].[content], [post].[userId] DESC', + string: 'SELECT [post].[content] FROM [post] ORDER BY [post].[content], [post].[userId] DESC' + }, + oracle: { + text : 'SELECT "post"."content" FROM "post" ORDER BY "post"."content", "post"."userId" DESC', + string: 'SELECT "post"."content" FROM "post" ORDER BY "post"."content", "post"."userId" DESC' + }, params: [] }); @@ -51,6 +68,14 @@ Harness.test({ text : 'SELECT `post`.`content` FROM `post` ORDER BY `post`.`content`, `post`.`userId` DESC', string: 'SELECT `post`.`content` FROM `post` ORDER BY `post`.`content`, `post`.`userId` DESC' }, + mssql: { + text : 'SELECT [post].[content] FROM [post] ORDER BY [post].[content], [post].[userId] DESC', + string: 'SELECT [post].[content] FROM [post] ORDER BY [post].[content], [post].[userId] DESC' + }, + oracle: { + text : 'SELECT "post"."content" FROM "post" ORDER BY "post"."content", "post"."userId" DESC', + string: 'SELECT "post"."content" FROM "post" ORDER BY "post"."content", "post"."userId" DESC' + }, params: [] }); @@ -68,6 +93,14 @@ Harness.test({ text : 'SELECT `post`.`content` FROM `post` ORDER BY `post`.`content`, `post`.`userId` DESC', string: 'SELECT `post`.`content` FROM `post` ORDER BY `post`.`content`, `post`.`userId` DESC' }, + mssql: { + text : 'SELECT [post].[content] FROM [post] ORDER BY [post].[content], [post].[userId] DESC', + string: 'SELECT [post].[content] FROM [post] ORDER BY [post].[content], [post].[userId] DESC' + }, + oracle: { + text : 'SELECT "post"."content" FROM "post" ORDER BY "post"."content", "post"."userId" DESC', + string: 'SELECT "post"."content" FROM "post" ORDER BY "post"."content", "post"."userId" DESC' + }, params: [] }); @@ -85,5 +118,248 @@ Harness.test({ text : 'SELECT `post`.`content` FROM `post` ORDER BY `post`.`content`, `post`.`userId` DESC', string: 'SELECT `post`.`content` FROM `post` ORDER BY `post`.`content`, `post`.`userId` DESC' }, + mssql: { + text : 'SELECT [post].[content] FROM [post] ORDER BY [post].[content], [post].[userId] DESC', + string: 'SELECT [post].[content] FROM [post] ORDER BY [post].[content], [post].[userId] DESC' + }, + oracle: { + text : 'SELECT "post"."content" FROM "post" ORDER BY "post"."content", "post"."userId" DESC', + string: 'SELECT "post"."content" FROM "post" ORDER BY "post"."content", "post"."userId" DESC' + }, + params: [] +}); + +Harness.test({ + query: post.select(post.content.isNull()).order(post.content.isNull()), + pg: { + text : 'SELECT ("post"."content" IS NULL) FROM "post" ORDER BY ("post"."content" IS NULL)', + string: 'SELECT ("post"."content" IS NULL) FROM "post" ORDER BY ("post"."content" IS NULL)' + }, + sqlite: { + text : 'SELECT ("post"."content" IS NULL) FROM "post" ORDER BY ("post"."content" IS NULL)', + string: 'SELECT ("post"."content" IS NULL) FROM "post" ORDER BY ("post"."content" IS NULL)' + }, + mysql: { + text : 'SELECT (`post`.`content` IS NULL) FROM `post` ORDER BY (`post`.`content` IS NULL)', + string: 'SELECT (`post`.`content` IS NULL) FROM `post` ORDER BY (`post`.`content` IS NULL)' + }, + mssql: { + text : 'SELECT ([post].[content] IS NULL) FROM [post] ORDER BY ([post].[content] IS NULL)', + string: 'SELECT ([post].[content] IS NULL) FROM [post] ORDER BY ([post].[content] IS NULL)' + }, + oracle: { + text : 'SELECT ("post"."content" IS NULL) FROM "post" ORDER BY ("post"."content" IS NULL)', + string: 'SELECT ("post"."content" IS NULL) FROM "post" ORDER BY ("post"."content" IS NULL)' + }, + params: [] +}); + +Harness.test({ + query: post.select(post.content.isNull()).order(post.content.isNull().descending()), + pg: { + text : 'SELECT ("post"."content" IS NULL) FROM "post" ORDER BY ("post"."content" IS NULL) DESC', + string: 'SELECT ("post"."content" IS NULL) FROM "post" ORDER BY ("post"."content" IS NULL) DESC' + }, + sqlite: { + text : 'SELECT ("post"."content" IS NULL) FROM "post" ORDER BY ("post"."content" IS NULL) DESC', + string: 'SELECT ("post"."content" IS NULL) FROM "post" ORDER BY ("post"."content" IS NULL) DESC' + }, + mysql: { + text : 'SELECT (`post`.`content` IS NULL) FROM `post` ORDER BY (`post`.`content` IS NULL) DESC', + string: 'SELECT (`post`.`content` IS NULL) FROM `post` ORDER BY (`post`.`content` IS NULL) DESC' + }, + mssql: { + text : 'SELECT ([post].[content] IS NULL) FROM [post] ORDER BY ([post].[content] IS NULL) DESC', + string: 'SELECT ([post].[content] IS NULL) FROM [post] ORDER BY ([post].[content] IS NULL) DESC' + }, + oracle: { + text : 'SELECT ("post"."content" IS NULL) FROM "post" ORDER BY ("post"."content" IS NULL) DESC', + string: 'SELECT ("post"."content" IS NULL) FROM "post" ORDER BY ("post"."content" IS NULL) DESC' + }, + params: [] +}); + +Harness.test({ + query: post.select(post.content.isNull()).order(post.content.isNull()), + pg: { + text : 'SELECT ("post"."content" IS NULL) FROM "post" ORDER BY ("post"."content" IS NULL)', + string: 'SELECT ("post"."content" IS NULL) FROM "post" ORDER BY ("post"."content" IS NULL)' + }, + sqlite: { + text : 'SELECT ("post"."content" IS NULL) FROM "post" ORDER BY ("post"."content" IS NULL)', + string: 'SELECT ("post"."content" IS NULL) FROM "post" ORDER BY ("post"."content" IS NULL)' + }, + mysql: { + text : 'SELECT (`post`.`content` IS NULL) FROM `post` ORDER BY (`post`.`content` IS NULL)', + string: 'SELECT (`post`.`content` IS NULL) FROM `post` ORDER BY (`post`.`content` IS NULL)' + }, + mssql: { + text : 'SELECT ([post].[content] IS NULL) FROM [post] ORDER BY ([post].[content] IS NULL)', + string: 'SELECT ([post].[content] IS NULL) FROM [post] ORDER BY ([post].[content] IS NULL)' + }, + oracle: { + text : 'SELECT ("post"."content" IS NULL) FROM "post" ORDER BY ("post"."content" IS NULL)', + string: 'SELECT ("post"."content" IS NULL) FROM "post" ORDER BY ("post"."content" IS NULL)' + }, + params: [] +}); + +Harness.test({ + query: post.select(sql.functions.RTRIM(post.content)).order(sql.functions.RTRIM(post.content)), + pg: { + text : 'SELECT RTRIM("post"."content") FROM "post" ORDER BY RTRIM("post"."content")', + string: 'SELECT RTRIM("post"."content") FROM "post" ORDER BY RTRIM("post"."content")' + }, + sqlite: { + text : 'SELECT RTRIM("post"."content") FROM "post" ORDER BY RTRIM("post"."content")', + string: 'SELECT RTRIM("post"."content") FROM "post" ORDER BY RTRIM("post"."content")' + }, + mysql: { + text : 'SELECT RTRIM(`post`.`content`) FROM `post` ORDER BY RTRIM(`post`.`content`)', + string: 'SELECT RTRIM(`post`.`content`) FROM `post` ORDER BY RTRIM(`post`.`content`)' + }, + mssql: { + text : 'SELECT RTRIM([post].[content]) FROM [post] ORDER BY RTRIM([post].[content])', + string: 'SELECT RTRIM([post].[content]) FROM [post] ORDER BY RTRIM([post].[content])' + }, + oracle: { + text : 'SELECT RTRIM("post"."content") FROM "post" ORDER BY RTRIM("post"."content")', + string: 'SELECT RTRIM("post"."content") FROM "post" ORDER BY RTRIM("post"."content")' + }, + params: [] +}); + +Harness.test({ + query: post.select(sql.functions.RTRIM(post.content)).order(sql.functions.RTRIM(post.content).descending()), + pg: { + text : 'SELECT RTRIM("post"."content") FROM "post" ORDER BY RTRIM("post"."content") DESC', + string: 'SELECT RTRIM("post"."content") FROM "post" ORDER BY RTRIM("post"."content") DESC' + }, + sqlite: { + text : 'SELECT RTRIM("post"."content") FROM "post" ORDER BY RTRIM("post"."content") DESC', + string: 'SELECT RTRIM("post"."content") FROM "post" ORDER BY RTRIM("post"."content") DESC' + }, + mysql: { + text : 'SELECT RTRIM(`post`.`content`) FROM `post` ORDER BY RTRIM(`post`.`content`) DESC', + string: 'SELECT RTRIM(`post`.`content`) FROM `post` ORDER BY RTRIM(`post`.`content`) DESC' + }, + mssql: { + text : 'SELECT RTRIM([post].[content]) FROM [post] ORDER BY RTRIM([post].[content]) DESC', + string: 'SELECT RTRIM([post].[content]) FROM [post] ORDER BY RTRIM([post].[content]) DESC' + }, + oracle: { + text : 'SELECT RTRIM("post"."content") FROM "post" ORDER BY RTRIM("post"."content") DESC', + string: 'SELECT RTRIM("post"."content") FROM "post" ORDER BY RTRIM("post"."content") DESC' + }, + params: [] +}); + +Harness.test({ + query: post.select(post.content).order(post.content.descending), + pg: { + text : 'SELECT "post"."content" FROM "post" ORDER BY "post"."content" DESC NULLS LAST', + string: 'SELECT "post"."content" FROM "post" ORDER BY "post"."content" DESC NULLS LAST', + config: { + nullOrder: "last" + } + }, + sqlite: { + text : 'SELECT "post"."content" FROM "post" ORDER BY "post"."content" DESC', + string: 'SELECT "post"."content" FROM "post" ORDER BY "post"."content" DESC' + }, + mysql: { + text : 'SELECT `post`.`content` FROM `post` ORDER BY `post`.`content` DESC', + string: 'SELECT `post`.`content` FROM `post` ORDER BY `post`.`content` DESC' + }, + mssql: { + text : 'SELECT [post].[content] FROM [post] ORDER BY [post].[content] DESC', + string: 'SELECT [post].[content] FROM [post] ORDER BY [post].[content] DESC' + }, + oracle: { + text : 'SELECT "post"."content" FROM "post" ORDER BY "post"."content" DESC', + string: 'SELECT "post"."content" FROM "post" ORDER BY "post"."content" DESC' + }, params: [] }); + +Harness.test({ + query: post.select(post.content).order(post.content), + pg: { + text : 'SELECT "post"."content" FROM "post" ORDER BY "post"."content" NULLS LAST', + string: 'SELECT "post"."content" FROM "post" ORDER BY "post"."content" NULLS LAST', + config: { + nullOrder: "last" + } + }, + sqlite: { + text : 'SELECT "post"."content" FROM "post" ORDER BY "post"."content"', + string: 'SELECT "post"."content" FROM "post" ORDER BY "post"."content"' + }, + mysql: { + text : 'SELECT `post`.`content` FROM `post` ORDER BY `post`.`content`', + string: 'SELECT `post`.`content` FROM `post` ORDER BY `post`.`content`' + }, + mssql: { + text : 'SELECT [post].[content] FROM [post] ORDER BY [post].[content]', + string: 'SELECT [post].[content] FROM [post] ORDER BY [post].[content]' + }, + oracle: { + text : 'SELECT "post"."content" FROM "post" ORDER BY "post"."content"', + string: 'SELECT "post"."content" FROM "post" ORDER BY "post"."content"' + }, + params: [] +}); + +Harness.test({ + query: post.select(post.content).order(post.content.asc), + pg: { + text : 'SELECT "post"."content" FROM "post" ORDER BY "post"."content" NULLS FIRST', + string: 'SELECT "post"."content" FROM "post" ORDER BY "post"."content" NULLS FIRST', + config: { + nullOrder: "first" + } + }, + sqlite: { + text : 'SELECT "post"."content" FROM "post" ORDER BY "post"."content"', + string: 'SELECT "post"."content" FROM "post" ORDER BY "post"."content"' + }, + mysql: { + text : 'SELECT `post`.`content` FROM `post` ORDER BY `post`.`content`', + string: 'SELECT `post`.`content` FROM `post` ORDER BY `post`.`content`' + }, + mssql: { + text : 'SELECT [post].[content] FROM [post] ORDER BY [post].[content]', + string: 'SELECT [post].[content] FROM [post] ORDER BY [post].[content]' + }, + oracle: { + text : 'SELECT "post"."content" FROM "post" ORDER BY "post"."content"', + string: 'SELECT "post"."content" FROM "post" ORDER BY "post"."content"' + }, + params: [] +}); + +Harness.test({ + query: post.select(post.content).order([]), + pg: { + text : 'SELECT "post"."content" FROM "post"', + string: 'SELECT "post"."content" FROM "post"' + }, + sqlite: { + text : 'SELECT "post"."content" FROM "post"', + string: 'SELECT "post"."content" FROM "post"' + }, + mysql: { + text : 'SELECT `post`.`content` FROM `post`', + string: 'SELECT `post`.`content` FROM `post`' + }, + mssql: { + text : 'SELECT [post].[content] FROM [post]', + string: 'SELECT [post].[content] FROM [post]' + }, + oracle: { + text : 'SELECT "post"."content" FROM "post"', + string: 'SELECT "post"."content" FROM "post"' + }, + params: [] +}); + diff --git a/test/dialects/regex-tests.js b/test/dialects/regex-tests.js new file mode 100644 index 00000000..40dde618 --- /dev/null +++ b/test/dialects/regex-tests.js @@ -0,0 +1,41 @@ +'use strict'; + +var Harness = require('./support'); +var customer = Harness.defineCustomerTable(); +var Sql = require('../../lib').setDialect('postgres'); + +Harness.test({ + query: customer.select(customer.metadata.regex('age')), + pg: { + text : 'SELECT ("customer"."metadata" ~ $1) FROM "customer"', + string: 'SELECT ("customer"."metadata" ~ \'age\') FROM "customer"' + }, + params: ['age'] +}); + +Harness.test({ + query: customer.select(customer.metadata.iregex('age')), + pg: { + text : 'SELECT ("customer"."metadata" ~* $1) FROM "customer"', + string: 'SELECT ("customer"."metadata" ~* \'age\') FROM "customer"' + }, + params: ['age'] +}); + +Harness.test({ + query: customer.select(customer.metadata.notRegex('age')), + pg: { + text : 'SELECT ("customer"."metadata" !~ $1) FROM "customer"', + string: 'SELECT ("customer"."metadata" !~ \'age\') FROM "customer"' + }, + params: ['age'] +}); + +Harness.test({ + query: customer.select(customer.metadata.notIregex('age')), + pg: { + text : 'SELECT ("customer"."metadata" !~* $1) FROM "customer"', + string: 'SELECT ("customer"."metadata" !~* \'age\') FROM "customer"' + }, + params: ['age'] +}); diff --git a/test/dialects/replace-tests.js b/test/dialects/replace-tests.js new file mode 100644 index 00000000..72af0e4b --- /dev/null +++ b/test/dialects/replace-tests.js @@ -0,0 +1,1000 @@ +'use strict'; + +var Harness = require('./support'); +var post = Harness.definePostTable(); +var user = Harness.defineUserTable(); +var contentTable = Harness.defineContentTable(); +var customerAliasTable = Harness.defineCustomerAliasTable(); + +var arrayTable = require('../../lib/table').define({ + name: 'arraytest', + columns: ['id', 'numbers'] +}); + +Harness.test({ + query: post.replace(post.content.value('test'), post.userId.value(1)), + pg: { + throws: true + }, + sqlite: { + text : 'REPLACE INTO "post" ("content", "userId") VALUES ($1, $2)', + string: 'REPLACE INTO "post" ("content", "userId") VALUES (\'test\', 1)' + }, + mysql: { + text : 'REPLACE INTO `post` (`content`, `userId`) VALUES (?, ?)', + string: 'REPLACE INTO `post` (`content`, `userId`) VALUES (\'test\', 1)' + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: ['test', 1] +}); + +Harness.test({ + query: post.replace(post.content.value('whoah')), + pg: { + throws: true + }, + sqlite: { + text : 'REPLACE INTO "post" ("content") VALUES ($1)', + string: 'REPLACE INTO "post" ("content") VALUES (\'whoah\')' + }, + mysql: { + text : 'REPLACE INTO `post` (`content`) VALUES (?)', + string: 'REPLACE INTO `post` (`content`) VALUES (\'whoah\')' + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: ['whoah'] +}); + +Harness.test({ + query: post.replace({length: 0}), + pg: { + throws: true + }, + sqlite: { + text : 'REPLACE INTO "post" ("length") VALUES ($1)', + string: 'REPLACE INTO "post" ("length") VALUES (0)' + }, + mysql: { + text : 'REPLACE INTO `post` (`length`) VALUES (?)', + string: 'REPLACE INTO `post` (`length`) VALUES (0)' + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: [0] +}); + +Harness.test({ + query: post.replace({ + content: 'test', + userId: 2 + }), + pg: { + throws: true + }, + sqlite: { + text : 'REPLACE INTO "post" ("content", "userId") VALUES ($1, $2)', + string: 'REPLACE INTO "post" ("content", "userId") VALUES (\'test\', 2)' + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: ['test', 2] +}); + +Harness.test({ + query: post.replace({ + content: post.sql.functions.LOWER('TEST'), + userId: 2 + }), + pg: { + throws: true + }, + sqlite: { + text : 'REPLACE INTO "post" ("content", "userId") VALUES (LOWER($1), $2)', + string: 'REPLACE INTO "post" ("content", "userId") VALUES (LOWER(\'TEST\'), 2)' + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: ['TEST', 2] +}); + +// allow bulk replace +Harness.test({ + query: post.replace([{ + content: 'whoah' + }, { + content: 'hey' + } + ]), + pg: { + throws: true + }, + sqlite: { + text : 'REPLACE INTO "post" ("content") VALUES ($1), ($2)', + string: 'REPLACE INTO "post" ("content") VALUES (\'whoah\'), (\'hey\')' + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: ['whoah', 'hey'] +}); + +Harness.test({ + query: post.replace([{ + content: 'whoah', + userId: 1 + }, { + content: 'hey', + userId: 2 + } + ]), + pg: { + throws: true + }, + sqlite: { + text : 'REPLACE INTO "post" ("content", "userId") VALUES ($1, $2), ($3, $4)', + string: 'REPLACE INTO "post" ("content", "userId") VALUES (\'whoah\', 1), (\'hey\', 2)' + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: ['whoah', 1, 'hey', 2] +}); + +// consistent order +Harness.test({ + query: post.replace([{ + content: 'whoah', + userId: 1 + }, { + userId: 2, + content: 'hey' + } + ]), + pg: { + throws: true + }, + sqlite: { + text : 'REPLACE INTO "post" ("content", "userId") VALUES ($1, $2), ($3, $4)', + string: 'REPLACE INTO "post" ("content", "userId") VALUES (\'whoah\', 1), (\'hey\', 2)' + }, + mysql: { + text : 'REPLACE INTO `post` (`content`, `userId`) VALUES (?, ?), (?, ?)', + string: 'REPLACE INTO `post` (`content`, `userId`) VALUES (\'whoah\', 1), (\'hey\', 2)' + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: ['whoah', 1, 'hey', 2] +}); + +Harness.test({ + query: post.replace({}), + pg: { + throws: true + }, + sqlite: { + text : 'REPLACE INTO "post" DEFAULT VALUES', + string: 'REPLACE INTO "post" DEFAULT VALUES' + }, + mysql: { + text : 'REPLACE INTO `post` () VALUES ()', + string: 'REPLACE INTO `post` () VALUES ()' + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: [] +}); + +Harness.test({ + query: post.replace({}).returning('*'), + pg: { + throws: true + }, + sqlite: { + throws: true + }, + mysql: { + throws: true + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: [] +}); + +Harness.test({ + query: post.replace({}).returning(post.star()), + pg: { + throws: true + }, + sqlite: { + throws: true + }, + mysql: { + throws: true + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: [] +}); + +Harness.test({ + query: post.replace({}).returning(post.id), + pg: { + throws: true + }, + sqlite: { + throws: true + }, + mysql: { + throws: true + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: [] +}); + +Harness.test({ + query: post.replace({}).returning(post.id, post.content), + pg: { + throws: true + }, + sqlite: { + throws: true + }, + mysql: { + throws: true + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: [] +}); + +Harness.test({ + query: post.replace({}).returning([post.id, post.content]), + pg: { + throws: true + }, + sqlite: { + throws: true + }, + mysql: { + throws: true + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: [] +}); + +// handle missing columns +Harness.test({ + query: post.replace([{ + content: 'whoah', + userId: 1 + }, { + content: 'hey' + } + ]), + pg: { + throws: true + }, + sqlite: { + text : 'Sqlite requires the same number of columns in each replace row', + throws: true + }, + mysql: { + text : 'REPLACE INTO `post` (`content`, `userId`) VALUES (?, ?), (?, DEFAULT)', + string: 'REPLACE INTO `post` (`content`, `userId`) VALUES (\'whoah\', 1), (\'hey\', DEFAULT)', + params: ['whoah', 1, 'hey'] + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, +}); + +Harness.test({ + query: post.replace([{ + userId: 1 + }, { + content: 'hey', + userId: 2 + } + ]), + pg: { + throws: true + }, + sqlite: { + text : 'Sqlite requires the same number of columns in each replace row', + throws: true + }, + mysql: { + text : 'REPLACE INTO `post` (`userId`, `content`) VALUES (?, DEFAULT), (?, ?)', + string: 'REPLACE INTO `post` (`userId`, `content`) VALUES (1, DEFAULT), (2, \'hey\')', + params: [1, 2, 'hey'] + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, +}); + +Harness.test({ + query: post.replace(post.content, post.userId) + .select('\'test\'', user.id).from(user).where(user.name.like('A%')), + pg: { + throws: true + }, + sqlite: { + text : 'REPLACE INTO "post" ("content", "userId") SELECT \'test\', "user"."id" FROM "user" WHERE ("user"."name" LIKE $1)', + string: 'REPLACE INTO "post" ("content", "userId") SELECT \'test\', "user"."id" FROM "user" WHERE ("user"."name" LIKE \'A%\')' + }, + mysql: { + text : 'REPLACE INTO `post` (`content`, `userId`) SELECT \'test\', `user`.`id` FROM `user` WHERE (`user`.`name` LIKE ?)', + string: 'REPLACE INTO `post` (`content`, `userId`) SELECT \'test\', `user`.`id` FROM `user` WHERE (`user`.`name` LIKE \'A%\')' + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: ['A%'] +}); + +Harness.test({ + query: post.replace([post.content, post.userId]) + .select('\'test\'', user.id).from(user).where(user.name.like('A%')), + pg: { + throws: true + }, + sqlite: { + text : 'REPLACE INTO "post" ("content", "userId") SELECT \'test\', "user"."id" FROM "user" WHERE ("user"."name" LIKE $1)', + string: 'REPLACE INTO "post" ("content", "userId") SELECT \'test\', "user"."id" FROM "user" WHERE ("user"."name" LIKE \'A%\')' + }, + mysql: { + text : 'REPLACE INTO `post` (`content`, `userId`) SELECT \'test\', `user`.`id` FROM `user` WHERE (`user`.`name` LIKE ?)', + string: 'REPLACE INTO `post` (`content`, `userId`) SELECT \'test\', `user`.`id` FROM `user` WHERE (`user`.`name` LIKE \'A%\')' + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: ['A%'] +}); + +Harness.test({ + query: post.replace(post.userId) + .select(user.id).from(user).where(user.name.like('A%')), + pg: { + throws: true + }, + sqlite: { + text : 'REPLACE INTO "post" ("userId") SELECT "user"."id" FROM "user" WHERE ("user"."name" LIKE $1)', + string: 'REPLACE INTO "post" ("userId") SELECT "user"."id" FROM "user" WHERE ("user"."name" LIKE \'A%\')' + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: ['A%'] +}); + +Harness.test({ + query: post.replace(post.userId) + .select(post.userId).from(user.join(post).on(user.id.equals(post.userId))).where(post.tags.like('A%')), + pg: { + throws: true + }, + sqlite: { + text : 'REPLACE INTO "post" ("userId") SELECT "post"."userId" FROM "user" INNER JOIN "post" ON ("user"."id" = "post"."userId") WHERE ("post"."tags" LIKE $1)', + string: 'REPLACE INTO "post" ("userId") SELECT "post"."userId" FROM "user" INNER JOIN "post" ON ("user"."id" = "post"."userId") WHERE ("post"."tags" LIKE \'A%\')' + }, + mysql: { + text : 'REPLACE INTO `post` (`userId`) SELECT `post`.`userId` FROM `user` INNER JOIN `post` ON (`user`.`id` = `post`.`userId`) WHERE (`post`.`tags` LIKE ?)', + string: 'REPLACE INTO `post` (`userId`) SELECT `post`.`userId` FROM `user` INNER JOIN `post` ON (`user`.`id` = `post`.`userId`) WHERE (`post`.`tags` LIKE \'A%\')' + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: ['A%'] +}); + +Harness.test({ + query: post.replace(post.userId).select(user.id).distinct().from(user), + pg: { + throws: true + }, + sqlite: { + text : 'REPLACE INTO "post" ("userId") SELECT DISTINCT "user"."id" FROM "user"', + string: 'REPLACE INTO "post" ("userId") SELECT DISTINCT "user"."id" FROM "user"' + }, + mysql: { + text : 'REPLACE INTO `post` (`userId`) SELECT DISTINCT `user`.`id` FROM `user`', + string: 'REPLACE INTO `post` (`userId`) SELECT DISTINCT `user`.`id` FROM `user`' + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: [] +}); + +// Binary replaces +Harness.test({ + query: post.replace(post.content.value(new Buffer('test')), post.userId.value(2)), + pg: { + throws: true + }, + sqlite: { + text : 'REPLACE INTO "post" ("content", "userId") VALUES ($1, $2)', + string: 'REPLACE INTO "post" ("content", "userId") VALUES (x\'74657374\', 2)' + }, + mysql: { + text : 'REPLACE INTO `post` (`content`, `userId`) VALUES (?, ?)', + string: 'REPLACE INTO `post` (`content`, `userId`) VALUES (x\'74657374\', 2)' + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: [new Buffer('test'), 2] +}); + +Harness.test({ + query: post.replace({ + content: new Buffer('test'), + userId: 2 + }), + pg: { + throws: true + }, + sqlite: { + text : 'REPLACE INTO "post" ("content", "userId") VALUES ($1, $2)', + string: 'REPLACE INTO "post" ("content", "userId") VALUES (x\'74657374\', 2)' + }, + mysql: { + text : 'REPLACE INTO `post` (`content`, `userId`) VALUES (?, ?)', + string: 'REPLACE INTO `post` (`content`, `userId`) VALUES (x\'74657374\', 2)' + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: [new Buffer('test'), 2] +}); + +Harness.test({ + query: post.replace([{ + content: new Buffer('whoah') + }, { + content: new Buffer('hey') + } + ]), + pg: { + throws: true + }, + sqlite: { + text : 'REPLACE INTO "post" ("content") VALUES ($1), ($2)', + string: 'REPLACE INTO "post" ("content") VALUES (x\'77686f6168\'), (x\'686579\')' + }, + mysql: { + text : 'REPLACE INTO `post` (`content`) VALUES (?), (?)', + string: 'REPLACE INTO `post` (`content`) VALUES (x\'77686f6168\'), (x\'686579\')' + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: [new Buffer('whoah'), new Buffer('hey')] +}); + +Harness.test({ + query: post.replace({ + content: 'test', + userId: 2 + }).onDuplicate({ + content: 'testupdate', + }), + pg: { + throws: true + }, + sqlite: { + throws: true + }, + mysql: { + text : 'REPLACE INTO `post` (`content`, `userId`) VALUES (?, ?) ON DUPLICATE KEY UPDATE `post`.`content` = ?', + string: 'REPLACE INTO `post` (`content`, `userId`) VALUES (\'test\', 2) ON DUPLICATE KEY UPDATE `post`.`content` = \'testupdate\'' + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: ['test', 2, 'testupdate'] +}); + +Harness.test({ + query: customerAliasTable.replace({ + id : 2, + name : 'test' + }).onConflict({ + columns: ['id'], + update: ['name'] + }), + mysql: { + throws: true + }, + sqlite: { + throws: true + }, + pg: { + throws: true + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: [2, 'test'] +}); + +Harness.test({ + query: customerAliasTable.replace({ + id : 2, + name : 'test' + }).orIgnore(), + mysql: { + throws: true + }, + sqlite: { + text : 'REPLACE OR IGNORE INTO "customer" ("id", "name") VALUES ($1, $2)', + string: 'REPLACE OR IGNORE INTO "customer" ("id", "name") VALUES (2, \'test\')' + }, + pg: { + throws: true + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: [2, 'test'] +}); + +Harness.test({ + query: post.replace({ + content: 'test', + userId: 2 + }).onConflict({ + columns: ['userId'], + update: ['content'] + }), + mysql: { + throws: true + }, + sqlite: { + throws: true + }, + pg: { + throws: true + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: ['test', 2] +}); + +Harness.test({ + query: post.replace({ + content: 'test', + userId: 2 + }).onConflict({ + columns: ['userId','content'], + update: ['content','userId'] + }), + mysql: { + throws: true + }, + sqlite: { + throws: true + }, + pg: { + throws: true + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: ['test', 2] +}); + +Harness.test({ + query: post.replace({ + content: 'test', + userId: 2 + }).onConflict({ + columns: ['userId'], + update: ['content'] + }).where(post.userId.equals(2)), + mysql: { + throws: true + }, + sqlite: { + throws: true + }, + pg: { + throws: true + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: ['test', 2, 2] +}); + +Harness.test({ + query: post.replace({ + content: 'test', + userId: 2 + }).onConflict({ + constraint: 'conc_userId', + update: ['content'] + }).where(post.userId.equals(2)), + mysql: { + throws: true + }, + sqlite: { + throws: true + }, + pg: { + throws: true + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: ['test', 2, 2] +}); + +Harness.test({ + query: post.replace({ + content: 'test', + userId: 2 + }).onConflict({ + columns: ['userId'], + }), + mysql: { + throws: true + }, + sqlite: { + throws: true + }, + pg: { + throws: true + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: ['test', 2] +}); + +Harness.test({ + query: post.replace({ + content: 'test', + userId: 2 + }).onConflict({ + constraint: 'conc_userId', + }), + mysql: { + throws: true + }, + sqlite: { + throws: true + }, + pg: { + throws: true + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: ['test', 2] +}); + +Harness.test({ + query: contentTable.replace({ + contentId: 20, + text : "something" + }).onConflict({ + columns: ['contentId'], + }), + mysql: { + throws: true + }, + sqlite: { + throws: true + }, + pg: { + throws: true + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: [20, "something"] +}); + +Harness.test({ + query: contentTable.replace({ + contentId: 20, + text : "something", + contentPosts : "another thing", + }).onConflict({ + columns: ['contentId'], + update: ['contentPosts'] + }), + mysql: { + throws: true + }, + sqlite: { + throws: true + }, + pg: { + throws: true + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: [20, "something", "another thing"] +}); + +Harness.test({ + query: post.replace([]), + + mysql: { + text : 'SELECT `post`.* FROM `post` WHERE (1=2)', + string: 'SELECT `post`.* FROM `post` WHERE (1=2)' + }, + params: [] +}); + +Harness.test({ + query: arrayTable.replace(arrayTable.id.value(1), arrayTable.numbers.value([2, 3, 4])), + pg: { + throws: true + }, + sqlite: { + text : 'REPLACE INTO "arraytest" ("id", "numbers") VALUES ($1, $2)', + string: 'REPLACE INTO "arraytest" ("id", "numbers") VALUES (1, \'[2,3,4]\')' + }, + mssql: { + throws: true + }, + oracle: { + throws: true + } +}); + +Harness.test({ + query: arrayTable.replace(arrayTable.id.value(1), arrayTable.numbers.value(["one", "two", "three"])), + pg: { + throws: true + }, + sqlite: { + text : 'REPLACE INTO "arraytest" ("id", "numbers") VALUES ($1, $2)', + string: 'REPLACE INTO "arraytest" ("id", "numbers") VALUES (1, \'["one","two","three"]\')' + }, + mssql: { + throws: true + }, + oracle: { + throws: true + } +}); + +Harness.test({ + query: post.replace(post.userId).select(user.id).from(user), + pg: { + throws: true + }, + sqlite: { + text : 'REPLACE INTO "post" ("userId") SELECT "user"."id" FROM "user"', + string: 'REPLACE INTO "post" ("userId") SELECT "user"."id" FROM "user"' + }, + mysql: { + text : 'REPLACE INTO `post` (`userId`) SELECT `user`.`id` FROM `user`', + string: 'REPLACE INTO `post` (`userId`) SELECT `user`.`id` FROM `user`' + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: [] +}); + +Harness.test({ + query: post.replace(post.userId).select(user.id).from(user).onConflict({ + columns: ['userId'], + update: ['content'] + }), + pg: { + throws: true + }, + sqlite: { + throws: true + }, + mysql: { + throws: true + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: [] +}); + +Harness.test({ + query: post.replace(post.userId).add(user.select(user.id)), + pg: { + throws: true + }, + sqlite: { + text : 'REPLACE INTO "post" ("userId") SELECT "user"."id" FROM "user"', + string: 'REPLACE INTO "post" ("userId") SELECT "user"."id" FROM "user"' + }, + mysql: { + text : 'REPLACE INTO `post` (`userId`) SELECT `user`.`id` FROM `user`', + string: 'REPLACE INTO `post` (`userId`) SELECT `user`.`id` FROM `user`' + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: [] +}); + +Harness.test({ + query: post.replace(post.userId).add(user.select(user.id).from(user)), + pg: { + throws: true + }, + sqlite: { + text : 'REPLACE INTO "post" ("userId") SELECT "user"."id" FROM "user"', + string: 'REPLACE INTO "post" ("userId") SELECT "user"."id" FROM "user"' + }, + mysql: { + text : 'REPLACE INTO `post` (`userId`) SELECT `user`.`id` FROM `user`', + string: 'REPLACE INTO `post` (`userId`) SELECT `user`.`id` FROM `user`' + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: [] +}); + +Harness.test({ + query: post.replace(post.userId).add(user.select(user.id).order(user.id)), + pg: { + throws: true + }, + sqlite: { + text : 'REPLACE INTO "post" ("userId") SELECT "user"."id" FROM "user" ORDER BY "user"."id"', + string: 'REPLACE INTO "post" ("userId") SELECT "user"."id" FROM "user" ORDER BY "user"."id"' + }, + mysql: { + text : 'REPLACE INTO `post` (`userId`) SELECT `user`.`id` FROM `user` ORDER BY `user`.`id`', + string: 'REPLACE INTO `post` (`userId`) SELECT `user`.`id` FROM `user` ORDER BY `user`.`id`' + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: [] +}); diff --git a/test/dialects/returning-tests.js b/test/dialects/returning-tests.js new file mode 100644 index 00000000..ebbb0b2e --- /dev/null +++ b/test/dialects/returning-tests.js @@ -0,0 +1,20 @@ +var Harness = require('./support'); +var user = Harness.defineUserTable(); + +Harness.test({ + query: user.insert({name: 'joe'}).returning(), + pg: { + text : 'INSERT INTO "user" ("name") VALUES ($1) RETURNING *', + string: 'INSERT INTO "user" ("name") VALUES (\'joe\') RETURNING *' + }, + params: ['joe'] +}); + +Harness.test({ + query: user.insert({name: 'joe'}).returning('id'), + pg: { + text : 'INSERT INTO "user" ("name") VALUES ($1) RETURNING id', + string: 'INSERT INTO "user" ("name") VALUES (\'joe\') RETURNING id' + }, + params: ['joe'] +}); diff --git a/test/dialects/schema-tests.js b/test/dialects/schema-tests.js index 1068a1ce..11e330da 100644 --- a/test/dialects/schema-tests.js +++ b/test/dialects/schema-tests.js @@ -24,6 +24,39 @@ Harness.test({ text : 'SELECT `staging`.`user`.`id` FROM `staging`.`user`', string: 'SELECT `staging`.`user`.`id` FROM `staging`.`user`' }, + mssql: { + text : 'SELECT [staging].[user].[id] FROM [staging].[user]', + string: 'SELECT [staging].[user].[id] FROM [staging].[user]' + }, + oracle: { + text : 'SELECT "staging"."user"."id" FROM "staging"."user"', + string: 'SELECT "staging"."user"."id" FROM "staging"."user"' + }, + params: [] +}); + +Harness.test({ + query: userWithSchema.select(userWithSchema.id.count()).from(userWithSchema), + pg: { + text : 'SELECT COUNT("staging"."user"."id") AS "id_count" FROM "staging"."user"', + string: 'SELECT COUNT("staging"."user"."id") AS "id_count" FROM "staging"."user"' + }, + sqlite: { + text : 'SELECT COUNT("staging"."user"."id") AS "id_count" FROM "staging"."user"', + string: 'SELECT COUNT("staging"."user"."id") AS "id_count" FROM "staging"."user"' + }, + mysql: { + text : 'SELECT COUNT(`staging`.`user`.`id`) AS `id_count` FROM `staging`.`user`', + string: 'SELECT COUNT(`staging`.`user`.`id`) AS `id_count` FROM `staging`.`user`' + }, + mssql: { + text : 'SELECT COUNT([staging].[user].[id]) AS [id_count] FROM [staging].[user]', + string: 'SELECT COUNT([staging].[user].[id]) AS [id_count] FROM [staging].[user]' + }, + oracle: { + text : 'SELECT COUNT("staging"."user"."id") "id_count" FROM "staging"."user"', + string: 'SELECT COUNT("staging"."user"."id") "id_count" FROM "staging"."user"' + }, params: [] }); @@ -41,6 +74,14 @@ Harness.test({ text : 'SELECT `staging`.`user`.`id`, `staging`.`user`.`name` FROM `staging`.`user`', string: 'SELECT `staging`.`user`.`id`, `staging`.`user`.`name` FROM `staging`.`user`' }, + mssql: { + text : 'SELECT [staging].[user].[id], [staging].[user].[name] FROM [staging].[user]', + string: 'SELECT [staging].[user].[id], [staging].[user].[name] FROM [staging].[user]' + }, + oracle: { + text : 'SELECT "staging"."user"."id", "staging"."user"."name" FROM "staging"."user"', + string: 'SELECT "staging"."user"."id", "staging"."user"."name" FROM "staging"."user"' + }, params: [] }); @@ -59,6 +100,14 @@ Harness.test({ text : 'SELECT `uws`.`name` FROM `staging`.`user` AS `uws`', string: 'SELECT `uws`.`name` FROM `staging`.`user` AS `uws`' }, + mssql: { + text : 'SELECT [uws].[name] FROM [staging].[user] AS [uws]', + string: 'SELECT [uws].[name] FROM [staging].[user] AS [uws]' + }, + oracle: { + text : 'SELECT "uws"."name" FROM "staging"."user" "uws"', + string: 'SELECT "uws"."name" FROM "staging"."user" "uws"' + }, params: [] }); @@ -82,6 +131,14 @@ Harness.test({ text : 'SELECT `staging`.`user`.`name`, `dev`.`post`.`content` FROM `staging`.`user` INNER JOIN `dev`.`post` ON (`staging`.`user`.`id` = `dev`.`post`.`userId`)', string: 'SELECT `staging`.`user`.`name`, `dev`.`post`.`content` FROM `staging`.`user` INNER JOIN `dev`.`post` ON (`staging`.`user`.`id` = `dev`.`post`.`userId`)' }, + mssql: { + text : 'SELECT [staging].[user].[name], [dev].[post].[content] FROM [staging].[user] INNER JOIN [dev].[post] ON ([staging].[user].[id] = [dev].[post].[userId])', + string: 'SELECT [staging].[user].[name], [dev].[post].[content] FROM [staging].[user] INNER JOIN [dev].[post] ON ([staging].[user].[id] = [dev].[post].[userId])' + }, + oracle: { + text : 'SELECT "staging"."user"."name", "dev"."post"."content" FROM "staging"."user" INNER JOIN "dev"."post" ON ("staging"."user"."id" = "dev"."post"."userId")', + string: 'SELECT "staging"."user"."name", "dev"."post"."content" FROM "staging"."user" INNER JOIN "dev"."post" ON ("staging"."user"."id" = "dev"."post"."userId")' + }, params: [] }); @@ -99,5 +156,13 @@ Harness.test({ text : 'SELECT `uws`.`name`, `dev`.`post`.`content` FROM `staging`.`user` AS `uws` INNER JOIN `dev`.`post` ON (`uws`.`id` = `dev`.`post`.`userId`)', string: 'SELECT `uws`.`name`, `dev`.`post`.`content` FROM `staging`.`user` AS `uws` INNER JOIN `dev`.`post` ON (`uws`.`id` = `dev`.`post`.`userId`)' }, + mssql: { + text : 'SELECT [uws].[name], [dev].[post].[content] FROM [staging].[user] AS [uws] INNER JOIN [dev].[post] ON ([uws].[id] = [dev].[post].[userId])', + string: 'SELECT [uws].[name], [dev].[post].[content] FROM [staging].[user] AS [uws] INNER JOIN [dev].[post] ON ([uws].[id] = [dev].[post].[userId])' + }, + oracle: { + text : 'SELECT "uws"."name", "dev"."post"."content" FROM "staging"."user" "uws" INNER JOIN "dev"."post" ON ("uws"."id" = "dev"."post"."userId")', + string: 'SELECT "uws"."name", "dev"."post"."content" FROM "staging"."user" "uws" INNER JOIN "dev"."post" ON ("uws"."id" = "dev"."post"."userId")' + }, params: [] }); diff --git a/test/dialects/select-tests.js b/test/dialects/select-tests.js index 0809574c..2892c8ae 100644 --- a/test/dialects/select-tests.js +++ b/test/dialects/select-tests.js @@ -2,6 +2,9 @@ var Harness = require('./support'); var post = Harness.definePostTable(); +var user = Harness.defineUserTable(); +var customerAlias = Harness.defineCustomerAliasTable(); +var Sql = require('../../lib'); Harness.test({ query: post.select(post.id).select(post.content), @@ -17,5 +20,442 @@ Harness.test({ text : 'SELECT `post`.`id`, `post`.`content` FROM `post`', string: 'SELECT `post`.`id`, `post`.`content` FROM `post`' }, + mssql: { + text : 'SELECT [post].[id], [post].[content] FROM [post]', + string: 'SELECT [post].[id], [post].[content] FROM [post]' + }, + oracle: { + text : 'SELECT "post"."id", "post"."content" FROM "post"', + string: 'SELECT "post"."id", "post"."content" FROM "post"' + }, + params: [] +}); + +Harness.test({ + query: customerAlias.select(customerAlias.star()), + pg: { + text : 'SELECT "customer"."id" AS "id_alias", "customer"."name" AS "name_alias", "customer"."age" AS "age_alias", "customer"."income" AS "income_alias", "customer"."metadata" AS "metadata_alias" FROM "customer"', + string: 'SELECT "customer"."id" AS "id_alias", "customer"."name" AS "name_alias", "customer"."age" AS "age_alias", "customer"."income" AS "income_alias", "customer"."metadata" AS "metadata_alias" FROM "customer"' + }, + sqlite: { + text : 'SELECT "customer"."id" AS "id_alias", "customer"."name" AS "name_alias", "customer"."age" AS "age_alias", "customer"."income" AS "income_alias", "customer"."metadata" AS "metadata_alias" FROM "customer"', + string: 'SELECT "customer"."id" AS "id_alias", "customer"."name" AS "name_alias", "customer"."age" AS "age_alias", "customer"."income" AS "income_alias", "customer"."metadata" AS "metadata_alias" FROM "customer"' + }, + mysql: { + text : 'SELECT `customer`.`id` AS `id_alias`, `customer`.`name` AS `name_alias`, `customer`.`age` AS `age_alias`, `customer`.`income` AS `income_alias`, `customer`.`metadata` AS `metadata_alias` FROM `customer`', + string: 'SELECT `customer`.`id` AS `id_alias`, `customer`.`name` AS `name_alias`, `customer`.`age` AS `age_alias`, `customer`.`income` AS `income_alias`, `customer`.`metadata` AS `metadata_alias` FROM `customer`' + }, + mssql: { + text : 'SELECT [customer].[id] AS [id_alias], [customer].[name] AS [name_alias], [customer].[age] AS [age_alias], [customer].[income] AS [income_alias], [customer].[metadata] AS [metadata_alias] FROM [customer]', + string: 'SELECT [customer].[id] AS [id_alias], [customer].[name] AS [name_alias], [customer].[age] AS [age_alias], [customer].[income] AS [income_alias], [customer].[metadata] AS [metadata_alias] FROM [customer]' + }, + oracle: { + text : 'SELECT "customer"."id" "id_alias", "customer"."name" "name_alias", "customer"."age" "age_alias", "customer"."income" "income_alias", "customer"."metadata" "metadata_alias" FROM "customer"', + string: 'SELECT "customer"."id" "id_alias", "customer"."name" "name_alias", "customer"."age" "age_alias", "customer"."income" "income_alias", "customer"."metadata" "metadata_alias" FROM "customer"' + }, + params: [] +}); + +// Test that we can generate a SELECT claus without a FROM clause +Harness.test({ + query: Sql.select(), + pg: { + text : 'SELECT ', + string: 'SELECT ' + }, + sqlite: { + text : 'SELECT ', + string: 'SELECT ' + }, + mysql: { + text : 'SELECT ', + string: 'SELECT ' + }, + mssql: { + text : 'SELECT ', + string: 'SELECT ' + }, + oracle: { + text : 'SELECT ', + string: 'SELECT ' + }, + params: [] +}); + +// Test that we can generate a SELECT claus without a FROM clause +Harness.test({ + query: Sql.select("1"), + pg: { + text : 'SELECT 1', + string: 'SELECT 1' + }, + sqlite: { + text : 'SELECT 1', + string: 'SELECT 1' + }, + mysql: { + text : 'SELECT 1', + string: 'SELECT 1' + }, + mssql: { + text : 'SELECT 1', + string: 'SELECT 1' + }, + oracle: { + text : 'SELECT 1', + string: 'SELECT 1' + }, + params: [] +}); + +Harness.test({ + query: Sql.select("1").where("1=1"), + pg: { + text : 'SELECT 1 WHERE (1=1)', + string: 'SELECT 1 WHERE (1=1)' + }, + sqlite: { + text : 'SELECT 1 WHERE (1=1)', + string: 'SELECT 1 WHERE (1=1)' + }, + mysql: { + text : 'SELECT 1 WHERE (1=1)', + string: 'SELECT 1 WHERE (1=1)' + }, + mssql: { + text : 'SELECT 1 WHERE (1=1)', + string: 'SELECT 1 WHERE (1=1)' + }, + oracle: { + text : 'SELECT 1 WHERE (1=1)', + string: 'SELECT 1 WHERE (1=1)' + }, + params: [] +}); + +Harness.test({ + query: Sql.select(post.select(post.id)), + pg: { + text : 'SELECT (SELECT "post"."id" FROM "post")', + string: 'SELECT (SELECT "post"."id" FROM "post")' + }, + sqlite: { + text : 'SELECT (SELECT "post"."id" FROM "post")', + string: 'SELECT (SELECT "post"."id" FROM "post")' + }, + mysql: { + text : 'SELECT (SELECT `post`.`id` FROM `post`)', + string: 'SELECT (SELECT `post`.`id` FROM `post`)' + }, + mssql: { + text : 'SELECT (SELECT [post].[id] FROM [post])', + string: 'SELECT (SELECT [post].[id] FROM [post])' + }, + oracle: { + text : 'SELECT (SELECT "post"."id" FROM "post")', + string: 'SELECT (SELECT "post"."id" FROM "post")' + }, params: [] }); + +Harness.test({ + query: Sql.select(post.select(post.content).plus(post.select(post.content))), + pg: { + text : 'SELECT ((SELECT "post"."content" FROM "post") + (SELECT "post"."content" FROM "post"))', + string: 'SELECT ((SELECT "post"."content" FROM "post") + (SELECT "post"."content" FROM "post"))' + }, + sqlite: { + text : 'SELECT ((SELECT "post"."content" FROM "post") + (SELECT "post"."content" FROM "post"))', + string: 'SELECT ((SELECT "post"."content" FROM "post") + (SELECT "post"."content" FROM "post"))' + }, + mysql: { + text : 'SELECT ((SELECT `post`.`content` FROM `post`) + (SELECT `post`.`content` FROM `post`))', + string: 'SELECT ((SELECT `post`.`content` FROM `post`) + (SELECT `post`.`content` FROM `post`))' + }, + mssql: { + text : 'SELECT ((SELECT [post].[content] FROM [post]) + (SELECT [post].[content] FROM [post]))', + string: 'SELECT ((SELECT [post].[content] FROM [post]) + (SELECT [post].[content] FROM [post]))' + }, + oracle: { + text : 'SELECT ((SELECT "post"."content" FROM "post") + (SELECT "post"."content" FROM "post"))', + string: 'SELECT ((SELECT "post"."content" FROM "post") + (SELECT "post"."content" FROM "post"))' + }, + params: [] +}); + +Harness.test({ + query: post.select(post.id.as('col1')), + pg: { + text : 'SELECT "post"."id" AS "col1" FROM "post"', + string: 'SELECT "post"."id" AS "col1" FROM "post"' + }, + sqlite: { + text : 'SELECT "post"."id" AS "col1" FROM "post"', + string: 'SELECT "post"."id" AS "col1" FROM "post"' + }, + mysql: { + text : 'SELECT `post`.`id` AS `col1` FROM `post`', + string: 'SELECT `post`.`id` AS `col1` FROM `post`' + }, + mssql: { + text : 'SELECT [post].[id] AS [col1] FROM [post]', + string: 'SELECT [post].[id] AS [col1] FROM [post]' + }, + oracle: { + text : 'SELECT "post"."id" "col1" FROM "post"', + string: 'SELECT "post"."id" "col1" FROM "post"' + }, + params: [] +}); + +Harness.test({ + query: post.select(Sql.constant(4)), + pg: { + text : 'SELECT $1 FROM "post"', + string: 'SELECT 4 FROM "post"' + }, + sqlite: { + text : 'SELECT $1 FROM "post"', + string: 'SELECT 4 FROM "post"' + }, + mysql: { + text : 'SELECT ? FROM `post`', + string: 'SELECT 4 FROM `post`' + }, + mssql: { + text : 'SELECT @1 FROM [post]', + string: 'SELECT 4 FROM [post]' + }, + oracle: { + text : 'SELECT :1 FROM "post"', + string: 'SELECT 4 FROM "post"' + }, + params: [4] +}); + +Harness.test({ + query: post.select(post.id,Sql.constant(4)), + pg: { + text : 'SELECT "post"."id", $1 FROM "post"', + string: 'SELECT "post"."id", 4 FROM "post"' + }, + sqlite: { + text : 'SELECT "post"."id", $1 FROM "post"', + string: 'SELECT "post"."id", 4 FROM "post"' + }, + mysql: { + text : 'SELECT `post`.`id`, ? FROM `post`', + string: 'SELECT `post`.`id`, 4 FROM `post`' + }, + mssql: { + text : 'SELECT [post].[id], @1 FROM [post]', + string: 'SELECT [post].[id], 4 FROM [post]' + }, + oracle: { + text : 'SELECT "post"."id", :1 FROM "post"', + string: 'SELECT "post"."id", 4 FROM "post"' + }, + params: [4] +}); + +Harness.test({ + query: post.select(Sql.constant(4).as('col1')), + pg: { + text : 'SELECT $1 AS "col1" FROM "post"', + string: 'SELECT 4 AS "col1" FROM "post"' + }, + sqlite: { + text : 'SELECT $1 AS "col1" FROM "post"', + string: 'SELECT 4 AS "col1" FROM "post"' + }, + mysql: { + text : 'SELECT ? AS `col1` FROM `post`', + string: 'SELECT 4 AS `col1` FROM `post`' + }, + mssql: { + text : 'SELECT @1 AS [col1] FROM [post]', + string: 'SELECT 4 AS [col1] FROM [post]' + }, + oracle: { + text : 'SELECT :1 "col1" FROM "post"', + string: 'SELECT 4 "col1" FROM "post"' + }, + params: [4] +}); + +Harness.test({ + query: post.select(Sql.constant(4).plus(5)), + pg: { + text : 'SELECT ($1 + $2) FROM "post"', + string: 'SELECT (4 + 5) FROM "post"' + }, + sqlite: { + text : 'SELECT ($1 + $2) FROM "post"', + string: 'SELECT (4 + 5) FROM "post"' + }, + mysql: { + text : 'SELECT (? + ?) FROM `post`', + string: 'SELECT (4 + 5) FROM `post`' + }, + mssql: { + text : 'SELECT (@1 + @2) FROM [post]', + string: 'SELECT (4 + 5) FROM [post]' + }, + oracle: { + text : 'SELECT (:1 + :2) FROM "post"', + string: 'SELECT (4 + 5) FROM "post"' + }, + params: [4,5] +}); + +Harness.test({ + query: post.select(Sql.constant(4).plus(5).as('col1')), + pg: { + text : 'SELECT ($1 + $2) AS "col1" FROM "post"', + string: 'SELECT (4 + 5) AS "col1" FROM "post"' + }, + sqlite: { + text : 'SELECT ($1 + $2) AS "col1" FROM "post"', + string: 'SELECT (4 + 5) AS "col1" FROM "post"' + }, + mysql: { + text : 'SELECT (? + ?) AS `col1` FROM `post`', + string: 'SELECT (4 + 5) AS `col1` FROM `post`' + }, + mssql: { + text : 'SELECT (@1 + @2) AS [col1] FROM [post]', + string: 'SELECT (4 + 5) AS [col1] FROM [post]' + }, + oracle: { + text : 'SELECT (:1 + :2) "col1" FROM "post"', + string: 'SELECT (4 + 5) "col1" FROM "post"' + }, + params: [4,5] +}); + +Harness.test({ + query: post.select(Sql.constant(4),Sql.constant("abc"),Sql.constant(true)), + pg: { + text : 'SELECT $1, $2, $3 FROM "post"', + string: 'SELECT 4, \'abc\', TRUE FROM "post"' + }, + sqlite: { + text : 'SELECT $1, $2, $3 FROM "post"', + string: 'SELECT 4, \'abc\', 1 FROM "post"' + }, + mysql: { + text : 'SELECT ?, ?, ? FROM `post`', + string: 'SELECT 4, \'abc\', TRUE FROM `post`' + }, + mssql: { + text : 'SELECT @1, @2, @3 FROM [post]', + string: 'SELECT 4, \'abc\', TRUE FROM [post]' + }, + oracle: { + text : 'SELECT :1, :2, :3 FROM "post"', + string: 'SELECT 4, \'abc\', TRUE FROM "post"' + }, + params: [4,'abc',true] +}); + +Harness.test({ + query: post.select(Sql.constant(1).sum()), + pg: { + text : 'SELECT SUM($1) AS "constant_sum" FROM "post"', + string: 'SELECT SUM(1) AS "constant_sum" FROM "post"' + }, + sqlite: { + text : 'SELECT SUM($1) AS "constant_sum" FROM "post"', + string: 'SELECT SUM(1) AS "constant_sum" FROM "post"' + }, + mysql: { + text : 'SELECT SUM(?) AS `constant_sum` FROM `post`', + string: 'SELECT SUM(1) AS `constant_sum` FROM `post`' + }, + mssql: { + text : 'SELECT SUM(@1) AS [constant_sum] FROM [post]', + string: 'SELECT SUM(1) AS [constant_sum] FROM [post]' + }, + oracle: { + text : 'SELECT SUM(:1) "constant_sum" FROM "post"', + string: 'SELECT SUM(1) "constant_sum" FROM "post"' + }, + params: [1] +}); + +Harness.test({ + query: Sql.select(post.select(post.id).as("column1")), + pg: { + text : 'SELECT (SELECT "post"."id" FROM "post") AS "column1"', + string: 'SELECT (SELECT "post"."id" FROM "post") AS "column1"' + }, + sqlite: { + text : 'SELECT (SELECT "post"."id" FROM "post") AS "column1"', + string: 'SELECT (SELECT "post"."id" FROM "post") AS "column1"' + }, + mysql: { + text : 'SELECT (SELECT `post`.`id` FROM `post`) AS `column1`', + string: 'SELECT (SELECT `post`.`id` FROM `post`) AS `column1`' + }, + mssql: { + text : 'SELECT (SELECT [post].[id] FROM [post]) AS [column1]', + string: 'SELECT (SELECT [post].[id] FROM [post]) AS [column1]' + }, + oracle: { + text : 'SELECT (SELECT "post"."id" FROM "post") "column1"', + string: 'SELECT (SELECT "post"."id" FROM "post") "column1"' + }, + params: [] +}); + +Harness.test({ + query: Sql.select(post.select(post.count()).as("column1")), + pg: { + text : 'SELECT (SELECT COUNT("post".*) AS "post_count" FROM "post") AS "column1"', + string: 'SELECT (SELECT COUNT("post".*) AS "post_count" FROM "post") AS "column1"' + }, + sqlite: { + text : 'SELECT (SELECT COUNT("post".*) AS "post_count" FROM "post") AS "column1"', + string: 'SELECT (SELECT COUNT("post".*) AS "post_count" FROM "post") AS "column1"' + }, + mysql: { + text : 'SELECT (SELECT COUNT(*) AS `post_count` FROM `post`) AS `column1`', + string: 'SELECT (SELECT COUNT(*) AS `post_count` FROM `post`) AS `column1`' + }, + mssql: { + text : 'SELECT (SELECT COUNT(*) AS [post_count] FROM [post]) AS [column1]', + string: 'SELECT (SELECT COUNT(*) AS [post_count] FROM [post]) AS [column1]' + }, + oracle: { + text : 'SELECT (SELECT COUNT(*) "post_count" FROM "post") "column1"', + string: 'SELECT (SELECT COUNT(*) "post_count" FROM "post") "column1"' + }, + params: [] +}); + +Harness.test({ + query: Sql.select(post.select(post.id).as("column1"),user.select(user.id).as("column2")), + pg: { + text : 'SELECT (SELECT "post"."id" FROM "post") AS "column1", (SELECT "user"."id" FROM "user") AS "column2"', + string: 'SELECT (SELECT "post"."id" FROM "post") AS "column1", (SELECT "user"."id" FROM "user") AS "column2"' + }, + sqlite: { + text : 'SELECT (SELECT "post"."id" FROM "post") AS "column1", (SELECT "user"."id" FROM "user") AS "column2"', + string: 'SELECT (SELECT "post"."id" FROM "post") AS "column1", (SELECT "user"."id" FROM "user") AS "column2"' + }, + mysql: { + text : 'SELECT (SELECT `post`.`id` FROM `post`) AS `column1`, (SELECT `user`.`id` FROM `user`) AS `column2`', + string: 'SELECT (SELECT `post`.`id` FROM `post`) AS `column1`, (SELECT `user`.`id` FROM `user`) AS `column2`' + }, + mssql: { + text : 'SELECT (SELECT [post].[id] FROM [post]) AS [column1], (SELECT [user].[id] FROM [user]) AS [column2]', + string: 'SELECT (SELECT [post].[id] FROM [post]) AS [column1], (SELECT [user].[id] FROM [user]) AS [column2]' + }, + oracle: { + text : 'SELECT (SELECT "post"."id" FROM "post") "column1", (SELECT "user"."id" FROM "user") "column2"', + string: 'SELECT (SELECT "post"."id" FROM "post") "column1", (SELECT "user"."id" FROM "user") "column2"' + }, + params: [] +}); + + diff --git a/test/dialects/shortcut-tests.js b/test/dialects/shortcut-tests.js index 62e27574..35c9a90b 100644 --- a/test/dialects/shortcut-tests.js +++ b/test/dialects/shortcut-tests.js @@ -19,6 +19,14 @@ Harness.test({ text : 'SELECT `user`.* FROM `user`', string: 'SELECT `user`.* FROM `user`' }, + mssql: { + text : 'SELECT [user].* FROM [user]', + string: 'SELECT [user].* FROM [user]' + }, + oracle: { + text : 'SELECT "user".* FROM "user"', + string: 'SELECT "user".* FROM "user"' + }, params: [] }); @@ -36,6 +44,14 @@ Harness.test({ text : 'SELECT * FROM `user` WHERE (`user`.`name` = ?)', string: 'SELECT * FROM `user` WHERE (`user`.`name` = 3)' }, + mssql: { + text : 'SELECT * FROM [user] WHERE ([user].[name] = @1)', + string: 'SELECT * FROM [user] WHERE ([user].[name] = 3)' + }, + oracle: { + text : 'SELECT * FROM "user" WHERE ("user"."name" = :1)', + string: 'SELECT * FROM "user" WHERE ("user"."name" = 3)' + }, params: [3] }); @@ -53,6 +69,14 @@ Harness.test({ text : 'SELECT * FROM `user` WHERE ((`user`.`name` = ?) AND (`user`.`id` = ?))', string: 'SELECT * FROM `user` WHERE ((`user`.`name` = 3) AND (`user`.`id` = 1))' }, + mssql: { + text : 'SELECT * FROM [user] WHERE (([user].[name] = @1) AND ([user].[id] = @2))', + string: 'SELECT * FROM [user] WHERE (([user].[name] = 3) AND ([user].[id] = 1))' + }, + oracle: { + text : 'SELECT * FROM "user" WHERE (("user"."name" = :1) AND ("user"."id" = :2))', + string: 'SELECT * FROM "user" WHERE (("user"."name" = 3) AND ("user"."id" = 1))' + }, params: [3, 1] }); @@ -71,6 +95,14 @@ Harness.test({ text : 'SELECT `post`.`content` FROM `post`', string: 'SELECT `post`.`content` FROM `post`' }, + mssql: { + text : 'SELECT [post].[content] FROM [post]', + string: 'SELECT [post].[content] FROM [post]' + }, + oracle: { + text : 'SELECT "post"."content" FROM "post"', + string: 'SELECT "post"."content" FROM "post"' + }, params: [] }); @@ -88,6 +120,14 @@ Harness.test({ text : 'SELECT `post`.`content` FROM `post` WHERE (`post`.`userId` = ?)', string: 'SELECT `post`.`content` FROM `post` WHERE (`post`.`userId` = 1)' }, + mssql: { + text : 'SELECT [post].[content] FROM [post] WHERE ([post].[userId] = @1)', + string: 'SELECT [post].[content] FROM [post] WHERE ([post].[userId] = 1)' + }, + oracle: { + text : 'SELECT "post"."content" FROM "post" WHERE ("post"."userId" = :1)', + string: 'SELECT "post"."content" FROM "post" WHERE ("post"."userId" = 1)' + }, params: [1] }); @@ -109,5 +149,13 @@ Harness.test({ text : 'SELECT * FROM `post` WHERE (((`post`.`content` IS NULL) OR (`post`.`content` = ?)) AND (`post`.`userId` = ?))', string: 'SELECT * FROM `post` WHERE (((`post`.`content` IS NULL) OR (`post`.`content` = \'\')) AND (`post`.`userId` = 1))' }, + mssql: { + text : 'SELECT * FROM [post] WHERE ((([post].[content] IS NULL) OR ([post].[content] = @1)) AND ([post].[userId] = @2))', + string: 'SELECT * FROM [post] WHERE ((([post].[content] IS NULL) OR ([post].[content] = \'\')) AND ([post].[userId] = 1))' + }, + oracle: { + text : 'SELECT * FROM "post" WHERE ((("post"."content" IS NULL) OR ("post"."content" = :1)) AND ("post"."userId" = :2))', + string: 'SELECT * FROM "post" WHERE ((("post"."content" IS NULL) OR ("post"."content" = \'\')) AND ("post"."userId" = 1))' + }, params: ['', 1] }); diff --git a/test/dialects/subfield-tests.js b/test/dialects/subfield-tests.js new file mode 100644 index 00000000..aeaab51d --- /dev/null +++ b/test/dialects/subfield-tests.js @@ -0,0 +1,42 @@ +'use strict'; + +var Harness = require('./support'); +var customer = Harness.defineCustomerCompositeTable(); +var Sql = require('../../lib').setDialect('postgres'); + +Harness.test({ + query: customer.select(customer.info.subfields.age), + pg: { + text : 'SELECT ("customer"."info")."age" FROM "customer"', + string: 'SELECT ("customer"."info")."age" FROM "customer"' + }, + params: [] +}); + + +Harness.test({ + query: customer.select(customer.info.subfields.age.as('years')), + pg: { + text : 'SELECT ("customer"."info")."age" AS "years" FROM "customer"', + string: 'SELECT ("customer"."info")."age" AS "years" FROM "customer"' + }, + params: [] +}); + +Harness.test({ + query: customer.select(customer.id).where(customer.info.subfields.salary.equals(10)), + pg: { + text : 'SELECT "customer"."id" FROM "customer" WHERE (("customer"."info")."salary" = $1)', + string: 'SELECT "customer"."id" FROM "customer" WHERE (("customer"."info")."salary" = 10)' + }, + params: [10] +}); + +Harness.test({ + query: customer.select(customer.info.subfields.name.distinct()), + pg: { + text : 'SELECT DISTINCT(("customer"."info")."name") FROM "customer"', + string: 'SELECT DISTINCT(("customer"."info")."name") FROM "customer"' + }, + params: [] +}); diff --git a/test/dialects/subquery-tests.js b/test/dialects/subquery-tests.js index 0d9e4159..05d76f1f 100644 --- a/test/dialects/subquery-tests.js +++ b/test/dialects/subquery-tests.js @@ -3,9 +3,34 @@ var Harness = require('./support'); var customer = Harness.defineCustomerTable(); var user = Harness.defineUserTable(); -var Table = require('../../lib/table'); +var post = Harness.definePostTable(); var Sql = require('../../lib'); +Harness.test({ + query: user.select(user.name).where(user.id.in(post.select(post.userId))), + pg: { + text: 'SELECT "user"."name" FROM "user" WHERE ("user"."id" IN (SELECT "post"."userId" FROM "post"))', + string: 'SELECT "user"."name" FROM "user" WHERE ("user"."id" IN (SELECT "post"."userId" FROM "post"))' + }, + sqlite: { + text: 'SELECT "user"."name" FROM "user" WHERE ("user"."id" IN (SELECT "post"."userId" FROM "post"))', + string: 'SELECT "user"."name" FROM "user" WHERE ("user"."id" IN (SELECT "post"."userId" FROM "post"))' + }, + mysql: { + text: 'SELECT `user`.`name` FROM `user` WHERE (`user`.`id` IN (SELECT `post`.`userId` FROM `post`))', + string: 'SELECT `user`.`name` FROM `user` WHERE (`user`.`id` IN (SELECT `post`.`userId` FROM `post`))' + }, + mssql: { + text: 'SELECT [user].[name] FROM [user] WHERE ([user].[id] IN (SELECT [post].[userId] FROM [post]))', + string: 'SELECT [user].[name] FROM [user] WHERE ([user].[id] IN (SELECT [post].[userId] FROM [post]))', + }, + oracle: { + text: 'SELECT "user"."name" FROM "user" WHERE ("user"."id" IN (SELECT "post"."userId" FROM "post"))', + string: 'SELECT "user"."name" FROM "user" WHERE ("user"."id" IN (SELECT "post"."userId" FROM "post"))' + }, + params: [] +}); + Harness.test({ query: user.name.in( customer.subQuery().select(customer.name).where( @@ -24,6 +49,14 @@ Harness.test({ text : '(`user`.`name` IN (SELECT `customer`.`name` FROM `customer` WHERE (`user`.`name` IN (SELECT `customer`.`name` FROM `customer` WHERE (`user`.`name` LIKE ?)))))', string: '(`user`.`name` IN (SELECT `customer`.`name` FROM `customer` WHERE (`user`.`name` IN (SELECT `customer`.`name` FROM `customer` WHERE (`user`.`name` LIKE \'%HELLO%\')))))' }, + mssql: { + text : '([user].[name] IN (SELECT [customer].[name] FROM [customer] WHERE ([user].[name] IN (SELECT [customer].[name] FROM [customer] WHERE ([user].[name] LIKE @1)))))', + string: '([user].[name] IN (SELECT [customer].[name] FROM [customer] WHERE ([user].[name] IN (SELECT [customer].[name] FROM [customer] WHERE ([user].[name] LIKE \'%HELLO%\')))))' + }, + oracle: { + text : '("user"."name" IN (SELECT "customer"."name" FROM "customer" WHERE ("user"."name" IN (SELECT "customer"."name" FROM "customer" WHERE ("user"."name" LIKE :1)))))', + string: '("user"."name" IN (SELECT "customer"."name" FROM "customer" WHERE ("user"."name" IN (SELECT "customer"."name" FROM "customer" WHERE ("user"."name" LIKE \'%HELLO%\')))))' + }, params: ['%HELLO%'] }); @@ -41,22 +74,68 @@ Harness.test({ text : 'SELECT * FROM (SELECT * FROM `user`)', string: 'SELECT * FROM (SELECT * FROM `user`)' }, + mssql: { + text : 'SELECT * FROM (SELECT * FROM [user])', + string: 'SELECT * FROM (SELECT * FROM [user])' + }, + oracle: { + text : 'SELECT * FROM (SELECT * FROM "user")', + string: 'SELECT * FROM (SELECT * FROM "user")' + }, params: [] }); +// Subquery with a date +Harness.test({ + query: Sql.select('*').from(post.subQuery().where(post.content.equals(new Date('Sat, 01 Jan 2000 00:00:00 GMT')))), + pg: { + text : 'SELECT * FROM (SELECT * FROM "post" WHERE ("post"."content" = $1))', + string: 'SELECT * FROM (SELECT * FROM "post" WHERE ("post"."content" = \'2000-01-01T00:00:00.000Z\'))' + }, + sqlite: { + text : 'SELECT * FROM (SELECT * FROM "post" WHERE ("post"."content" = $1))', + string: 'SELECT * FROM (SELECT * FROM "post" WHERE ("post"."content" = 946684800000))', + config: { + dateTimeMillis: true + } + }, + mysql: { + text : 'SELECT * FROM (SELECT * FROM `post` WHERE (`post`.`content` = ?))', + string: 'SELECT * FROM (SELECT * FROM `post` WHERE (`post`.`content` = \'2000-01-01T00:00:00.000Z\'))' + }, + mssql: { + text : 'SELECT * FROM (SELECT * FROM [post] WHERE ([post].[content] = @1))', + string: 'SELECT * FROM (SELECT * FROM [post] WHERE ([post].[content] = \'2000-01-01T00:00:00.000Z\'))' + }, + oracle: { + text : 'SELECT * FROM (SELECT * FROM "post" WHERE ("post"."content" = :1))', + string: 'SELECT * FROM (SELECT * FROM "post" WHERE ("post"."content" = \'2000-01-01T00:00:00.000Z\'))' + }, + params: [new Date('Sat, 01 Jan 2000 00:00:00 GMT')] +}); + + Harness.test({ query: Sql.select('*').from(customer.subQuery('T1')).from(user.subQuery('T2')), pg: { - text : 'SELECT * FROM (SELECT * FROM "customer") T1 , (SELECT * FROM "user") T2', - string: 'SELECT * FROM (SELECT * FROM "customer") T1 , (SELECT * FROM "user") T2' + text : 'SELECT * FROM (SELECT * FROM "customer") "T1" , (SELECT * FROM "user") "T2"', + string: 'SELECT * FROM (SELECT * FROM "customer") "T1" , (SELECT * FROM "user") "T2"' }, sqlite: { - text : 'SELECT * FROM (SELECT * FROM "customer") T1 , (SELECT * FROM "user") T2', - string: 'SELECT * FROM (SELECT * FROM "customer") T1 , (SELECT * FROM "user") T2' + text : 'SELECT * FROM (SELECT * FROM "customer") "T1" , (SELECT * FROM "user") "T2"', + string: 'SELECT * FROM (SELECT * FROM "customer") "T1" , (SELECT * FROM "user") "T2"' }, mysql: { - text : 'SELECT * FROM (SELECT * FROM `customer`) T1 , (SELECT * FROM `user`) T2', - string: 'SELECT * FROM (SELECT * FROM `customer`) T1 , (SELECT * FROM `user`) T2' + text : 'SELECT * FROM (SELECT * FROM `customer`) `T1` , (SELECT * FROM `user`) `T2`', + string: 'SELECT * FROM (SELECT * FROM `customer`) `T1` , (SELECT * FROM `user`) `T2`' + }, + mssql: { + text : 'SELECT * FROM (SELECT * FROM [customer]) [T1] , (SELECT * FROM [user]) [T2]', + string: 'SELECT * FROM (SELECT * FROM [customer]) [T1] , (SELECT * FROM [user]) [T2]' + }, + oracle: { + text : 'SELECT * FROM (SELECT * FROM "customer") "T1" , (SELECT * FROM "user") "T2"', + string: 'SELECT * FROM (SELECT * FROM "customer") "T1" , (SELECT * FROM "user") "T2"' }, params: [] }); @@ -78,6 +157,14 @@ Harness.test({ text : '(`customer`.`name` BETWEEN (SELECT MIN(`customer`.`name`) FROM `customer`) AND (SELECT MAX(`customer`.`name`) FROM `customer`))', string: '(`customer`.`name` BETWEEN (SELECT MIN(`customer`.`name`) FROM `customer`) AND (SELECT MAX(`customer`.`name`) FROM `customer`))' }, + mssql: { + text : '([customer].[name] BETWEEN (SELECT MIN([customer].[name]) FROM [customer]) AND (SELECT MAX([customer].[name]) FROM [customer]))', + string: '([customer].[name] BETWEEN (SELECT MIN([customer].[name]) FROM [customer]) AND (SELECT MAX([customer].[name]) FROM [customer]))' + }, + oracle: { + text : '("customer"."name" BETWEEN (SELECT MIN("customer"."name") FROM "customer") AND (SELECT MAX("customer"."name") FROM "customer"))', + string: '("customer"."name" BETWEEN (SELECT MIN("customer"."name") FROM "customer") AND (SELECT MAX("customer"."name") FROM "customer"))' + }, params: [] }); @@ -95,5 +182,92 @@ Harness.test({ text : '(EXISTS (SELECT * FROM `user` WHERE (`user`.`name` = `customer`.`name`)))', string: '(EXISTS (SELECT * FROM `user` WHERE (`user`.`name` = `customer`.`name`)))' }, + mssql: { + text : '(EXISTS (SELECT * FROM [user] WHERE ([user].[name] = [customer].[name])))', + string: '(EXISTS (SELECT * FROM [user] WHERE ([user].[name] = [customer].[name])))' + }, + oracle: { + text : '(EXISTS (SELECT * FROM "user" WHERE ("user"."name" = "customer"."name")))', + string: '(EXISTS (SELECT * FROM "user" WHERE ("user"."name" = "customer"."name")))' + }, + params: [] +}); + +var limitUsers = user.subQuery('limit-users').select(user.id, user.name).from(user).order(user.name).limit(10).offset(10); +Harness.test({ + query: Sql.select(limitUsers.name, post.tags).from(limitUsers.leftJoin(post).on(post.userId.equals(limitUsers.id))), + pg: { + text : 'SELECT "limit-users"."name", "post"."tags" FROM (SELECT "user"."id", "user"."name" FROM "user" ORDER BY "user"."name" LIMIT 10 OFFSET 10) "limit-users" LEFT JOIN "post" ON ("post"."userId" = "limit-users"."id")', + string: 'SELECT "limit-users"."name", "post"."tags" FROM (SELECT "user"."id", "user"."name" FROM "user" ORDER BY "user"."name" LIMIT 10 OFFSET 10) "limit-users" LEFT JOIN "post" ON ("post"."userId" = "limit-users"."id")' + }, + sqlite: { + text : 'SELECT "limit-users"."name", "post"."tags" FROM (SELECT "user"."id", "user"."name" FROM "user" ORDER BY "user"."name" LIMIT 10 OFFSET 10) "limit-users" LEFT JOIN "post" ON ("post"."userId" = "limit-users"."id")', + string: 'SELECT "limit-users"."name", "post"."tags" FROM (SELECT "user"."id", "user"."name" FROM "user" ORDER BY "user"."name" LIMIT 10 OFFSET 10) "limit-users" LEFT JOIN "post" ON ("post"."userId" = "limit-users"."id")' + }, + mysql: { + text : 'SELECT `limit-users`.`name`, `post`.`tags` FROM (SELECT `user`.`id`, `user`.`name` FROM `user` ORDER BY `user`.`name` LIMIT 10 OFFSET 10) `limit-users` LEFT JOIN `post` ON (`post`.`userId` = `limit-users`.`id`)', + string: 'SELECT `limit-users`.`name`, `post`.`tags` FROM (SELECT `user`.`id`, `user`.`name` FROM `user` ORDER BY `user`.`name` LIMIT 10 OFFSET 10) `limit-users` LEFT JOIN `post` ON (`post`.`userId` = `limit-users`.`id`)' + }, + mssql: { + text : 'SELECT [limit-users].[name], [post].[tags] FROM (SELECT [user].[id], [user].[name] FROM [user] ORDER BY [user].[name] OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY) [limit-users] LEFT JOIN [post] ON ([post].[userId] = [limit-users].[id])', + string: 'SELECT [limit-users].[name], [post].[tags] FROM (SELECT [user].[id], [user].[name] FROM [user] ORDER BY [user].[name] OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY) [limit-users] LEFT JOIN [post] ON ([post].[userId] = [limit-users].[id])' + }, + oracle: { + text : 'SELECT "limit-users"."name", "post"."tags" FROM (SELECT "user"."id", "user"."name" FROM "user" ORDER BY "user"."name" OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY) "limit-users" LEFT JOIN "post" ON ("post"."userId" = "limit-users"."id")', + string: 'SELECT "limit-users"."name", "post"."tags" FROM (SELECT "user"."id", "user"."name" FROM "user" ORDER BY "user"."name" OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY) "limit-users" LEFT JOIN "post" ON ("post"."userId" = "limit-users"."id")' + }, params: [] }); + +// Top-level subQuery +Harness.test({ + query: Sql.subQuery().select(user.id).from(user), + pg: { + text : '(SELECT "user"."id" FROM "user")', + string: '(SELECT "user"."id" FROM "user")' + }, + sqlite: { + text : '(SELECT "user"."id" FROM "user")', + string: '(SELECT "user"."id" FROM "user")' + }, + mysql: { + text : '(SELECT `user`.`id` FROM `user`)', + string: '(SELECT `user`.`id` FROM `user`)' + }, + mssql: { + text : '(SELECT [user].[id] FROM [user])', + string: '(SELECT [user].[id] FROM [user])' + }, + oracle: { + text : '(SELECT "user"."id" FROM "user")', + string: '(SELECT "user"."id" FROM "user")' + }, + params: [] +}); + +// Top-level subQuery with alias +Harness.test({ + query: Sql.subQuery("x").select(user.id).from(user), + pg: { + text : '(SELECT "user"."id" FROM "user") "x"', + string: '(SELECT "user"."id" FROM "user") "x"' + }, + sqlite: { + text : '(SELECT "user"."id" FROM "user") "x"', + string: '(SELECT "user"."id" FROM "user") "x"' + }, + mysql: { + text : '(SELECT `user`.`id` FROM `user`) `x`', + string: '(SELECT `user`.`id` FROM `user`) `x`' + }, + mssql: { + text : '(SELECT [user].[id] FROM [user]) [x]', + string: '(SELECT [user].[id] FROM [user]) [x]' + }, + oracle: { + text : '(SELECT "user"."id" FROM "user") "x"', + string: '(SELECT "user"."id" FROM "user") "x"' + }, + params: [] +}); + diff --git a/test/dialects/support.js b/test/dialects/support.js index a5b48e59..4a9fa427 100644 --- a/test/dialects/support.js +++ b/test/dialects/support.js @@ -1,4 +1,3 @@ -/* global test */ 'use strict'; var assert = require('assert'); @@ -8,7 +7,9 @@ var Table = require('../../lib/table'); var dialects = { pg : require('../../lib/dialect/postgres'), sqlite : require('../../lib/dialect/sqlite'), - mysql : require('../../lib/dialect/mysql') + mysql : require('../../lib/dialect/mysql'), + mssql : require('../../lib/dialect/mssql'), + oracle : require('../../lib/dialect/oracle') }; module.exports = { @@ -26,15 +27,15 @@ module.exports = { // check if this query is expected to throw if (expectedObject.throws) { assert.throws(function() { - new DialectClass().getQuery(expected.query); + new DialectClass(expectedObject.config).getQuery(expected.query); }); } else { // build query for dialect - var compiledQuery = new DialectClass().getQuery(expected.query); + var compiledQuery = new DialectClass(expectedObject.config).getQuery(expected.query); // test result is correct var expectedText = expectedObject.text || expectedObject; - assert.equal(compiledQuery.text, expectedText, 'query result'); + assert.equal(compiledQuery.text, expectedText); // if params are specified then test these are correct var expectedParams = expectedObject.params || expected.params; @@ -50,10 +51,10 @@ module.exports = { // test the toString if (expectedObject.throws) { assert.throws(function() { - new DialectClass().getString(expected.query); + new DialectClass(expectedObject.config).getString(expected.query); }); } else { - var compiledString = new DialectClass().getString(expected.query); + var compiledString = new DialectClass(expectedObject.config).getString(expected.query); // test result is correct assert.equal(compiledString, expectedObject.string); @@ -75,7 +76,7 @@ module.exports = { definePostTable: function() { return Table.define({ name: 'post', - columns: ['id', 'userId', 'content'] + columns: ['id', 'userId', 'content', 'tags', 'length'] }); }, @@ -89,7 +90,31 @@ module.exports = { defineCustomerTable: function() { return Table.define({ name: 'customer', - columns: ['id', 'name', 'age', 'income'] + columns: ['id', 'name', 'age', 'income', 'metadata'] + }); + }, + + // This table defines the customer attributes as a composite field + defineCustomerCompositeTable: function() { + return Table.define({ + name: 'customer', + columns: { + id: {}, + info: {subfields: ['name', 'age', 'salary']} + } + }); + }, + + defineCustomerAliasTable: function() { + return Table.define({ + name: 'customer', + columns: { + id: {property: 'id_alias'}, + name: {property: 'name_alias'}, + age: {property: 'age_alias'}, + income: {property: 'income_alias'}, + metadata: {property: 'metadata_alias'} + } }); }, @@ -99,5 +124,14 @@ module.exports = { name: 'variable', columns: ['a', 'b', 'c', 'd', 't', 'u', 'v', 'x', 'y', 'z'] }); + }, + +// this table is for testing snakeName related stuff + defineContentTable: function() { + return Table.define({ + name: 'content', + columns: ['content_id', 'text', 'content_posts'], + snakeToCamel: true + }); } }; diff --git a/test/dialects/table-tests.js b/test/dialects/table-tests.js index 01c2c1aa..6740a7c8 100644 --- a/test/dialects/table-tests.js +++ b/test/dialects/table-tests.js @@ -17,6 +17,14 @@ Harness.test({ text : 'SELECT `user`.`id` FROM `user`', string: 'SELECT `user`.`id` FROM `user`' }, + mssql: { + text : 'SELECT [user].[id] FROM [user]', + string: 'SELECT [user].[id] FROM [user]' + }, + oracle: { + text : 'SELECT "user"."id" FROM "user"', + string: 'SELECT "user"."id" FROM "user"' + }, params: [] }); @@ -34,6 +42,14 @@ Harness.test({ text : 'SELECT `user`.`id`, `user`.`name` FROM `user`', string: 'SELECT `user`.`id`, `user`.`name` FROM `user`' }, + mssql: { + text : 'SELECT [user].[id], [user].[name] FROM [user]', + string: 'SELECT [user].[id], [user].[name] FROM [user]' + }, + oracle: { + text : 'SELECT "user"."id", "user"."name" FROM "user"', + string: 'SELECT "user"."id", "user"."name" FROM "user"' + }, params: [] }); @@ -51,6 +67,98 @@ Harness.test({ text : 'SELECT `user`.* FROM `user`', string: 'SELECT `user`.* FROM `user`' }, + mssql: { + text : 'SELECT [user].* FROM [user]', + string: 'SELECT [user].* FROM [user]' + }, + oracle: { + text : 'SELECT "user".* FROM "user"', + string: 'SELECT "user".* FROM "user"' + }, + params: [] +}); + +Harness.test({ + query: user.select(user.id, [ user.name ]).from(user), + pg: { + text: 'SELECT "user"."id", "user"."name" FROM "user"', + string: 'SELECT "user"."id", "user"."name" FROM "user"' + }, + sqlite: { + text: 'SELECT "user"."id", "user"."name" FROM "user"', + string: 'SELECT "user"."id", "user"."name" FROM "user"' + }, + mysql: { + text: 'SELECT `user`.`id`, `user`.`name` FROM `user`', + string: 'SELECT `user`.`id`, `user`.`name` FROM `user`' + }, + oracle: { + text: 'SELECT "user"."id", "user"."name" FROM "user"', + string: 'SELECT "user"."id", "user"."name" FROM "user"' + }, + params: [] +}); + +Harness.test({ + query: user.select([ user.id ], user.name).from(user), + pg: { + text: 'SELECT "user"."id", "user"."name" FROM "user"', + string: 'SELECT "user"."id", "user"."name" FROM "user"' + }, + sqlite: { + text: 'SELECT "user"."id", "user"."name" FROM "user"', + string: 'SELECT "user"."id", "user"."name" FROM "user"' + }, + mysql: { + text: 'SELECT `user`.`id`, `user`.`name` FROM `user`', + string: 'SELECT `user`.`id`, `user`.`name` FROM `user`' + }, + oracle: { + text: 'SELECT "user"."id", "user"."name" FROM "user"', + string: 'SELECT "user"."id", "user"."name" FROM "user"' + }, + params: [] +}); + +Harness.test({ + query: user.select([ user.id , user.name ]).from(user), + pg: { + text: 'SELECT "user"."id", "user"."name" FROM "user"', + string: 'SELECT "user"."id", "user"."name" FROM "user"' + }, + sqlite: { + text: 'SELECT "user"."id", "user"."name" FROM "user"', + string: 'SELECT "user"."id", "user"."name" FROM "user"' + }, + mysql: { + text: 'SELECT `user`.`id`, `user`.`name` FROM `user`', + string: 'SELECT `user`.`id`, `user`.`name` FROM `user`' + }, + oracle: { + text: 'SELECT "user"."id", "user"."name" FROM "user"', + string: 'SELECT "user"."id", "user"."name" FROM "user"' + }, + params: [] +}); + +Harness.test({ + query: user.select(user.star(), user.star({ prefix: 'foo_' }), user.star({ prefix: 'bar_' })).from(user), + pg: { + text: 'SELECT "user".*, "user"."id" AS "foo_id", "user"."name" AS "foo_name", "user"."id" AS "bar_id", "user"."name" AS "bar_name" FROM "user"', + string: 'SELECT "user".*, "user"."id" AS "foo_id", "user"."name" AS "foo_name", "user"."id" AS "bar_id", "user"."name" AS "bar_name" FROM "user"' + }, + sqlite: { + text: 'SELECT "user".*, "user"."id" AS "foo_id", "user"."name" AS "foo_name", "user"."id" AS "bar_id", "user"."name" AS "bar_name" FROM "user"', + string: 'SELECT "user".*, "user"."id" AS "foo_id", "user"."name" AS "foo_name", "user"."id" AS "bar_id", "user"."name" AS "bar_name" FROM "user"' + }, + mysql: { + text: 'SELECT `user`.*, `user`.`id` AS `foo_id`, `user`.`name` AS `foo_name`, `user`.`id` AS `bar_id`, `user`.`name` AS `bar_name` FROM `user`', + string: 'SELECT `user`.*, `user`.`id` AS `foo_id`, `user`.`name` AS `foo_name`, `user`.`id` AS `bar_id`, `user`.`name` AS `bar_name` FROM `user`' + }, + oracle: { + text: 'SELECT "user".*, "user"."id" "foo_id", "user"."name" "foo_name", "user"."id" "bar_id", "user"."name" "bar_name" FROM "user"', + string: 'SELECT "user".*, "user"."id" "foo_id", "user"."name" "foo_name", "user"."id" "bar_id", "user"."name" "bar_name" FROM "user"' + }, params: [] }); @@ -68,6 +176,14 @@ Harness.test({ text : 'SELECT `user`.`id` FROM `user` WHERE (`user`.`name` = ?)', string: 'SELECT `user`.`id` FROM `user` WHERE (`user`.`name` = \'foo\')' }, + mssql: { + text : 'SELECT [user].[id] FROM [user] WHERE ([user].[name] = @1)', + string: 'SELECT [user].[id] FROM [user] WHERE ([user].[name] = \'foo\')' + }, + oracle: { + text : 'SELECT "user"."id" FROM "user" WHERE ("user"."name" = :1)', + string: 'SELECT "user"."id" FROM "user" WHERE ("user"."name" = \'foo\')' + }, params: ['foo'] }); @@ -85,6 +201,14 @@ Harness.test({ text : 'SELECT `user`.`id` FROM `user` WHERE ((`user`.`name` = ?) OR (`user`.`name` = ?))', string: 'SELECT `user`.`id` FROM `user` WHERE ((`user`.`name` = \'foo\') OR (`user`.`name` = \'bar\'))' }, + mssql: { + text : 'SELECT [user].[id] FROM [user] WHERE (([user].[name] = @1) OR ([user].[name] = @2))', + string: 'SELECT [user].[id] FROM [user] WHERE (([user].[name] = \'foo\') OR ([user].[name] = \'bar\'))' + }, + oracle: { + text : 'SELECT "user"."id" FROM "user" WHERE (("user"."name" = :1) OR ("user"."name" = :2))', + string: 'SELECT "user"."id" FROM "user" WHERE (("user"."name" = \'foo\') OR ("user"."name" = \'bar\'))' + }, params: ['foo', 'bar'] }); @@ -102,6 +226,14 @@ Harness.test({ text : 'SELECT `user`.`id` FROM `user` WHERE ((`user`.`name` = ?) AND (`user`.`name` = ?))', string: 'SELECT `user`.`id` FROM `user` WHERE ((`user`.`name` = \'foo\') AND (`user`.`name` = \'bar\'))' }, + mssql: { + text : 'SELECT [user].[id] FROM [user] WHERE (([user].[name] = @1) AND ([user].[name] = @2))', + string: 'SELECT [user].[id] FROM [user] WHERE (([user].[name] = \'foo\') AND ([user].[name] = \'bar\'))' + }, + oracle: { + text : 'SELECT "user"."id" FROM "user" WHERE (("user"."name" = :1) AND ("user"."name" = :2))', + string: 'SELECT "user"."id" FROM "user" WHERE (("user"."name" = \'foo\') AND ("user"."name" = \'bar\'))' + }, params: ['foo', 'bar'] }); @@ -119,6 +251,14 @@ Harness.test({ text : 'SELECT `user`.`id` FROM `user` WHERE ((`user`.`name` = ?) OR (`user`.`name` = ?))', string: 'SELECT `user`.`id` FROM `user` WHERE ((`user`.`name` = \'foo\') OR (`user`.`name` = \'bar\'))' }, + mssql: { + text : 'SELECT [user].[id] FROM [user] WHERE (([user].[name] = @1) OR ([user].[name] = @2))', + string: 'SELECT [user].[id] FROM [user] WHERE (([user].[name] = \'foo\') OR ([user].[name] = \'bar\'))' + }, + oracle: { + text : 'SELECT "user"."id" FROM "user" WHERE (("user"."name" = :1) OR ("user"."name" = :2))', + string: 'SELECT "user"."id" FROM "user" WHERE (("user"."name" = \'foo\') OR ("user"."name" = \'bar\'))' + }, params: ['foo', 'bar'] }); @@ -136,6 +276,14 @@ Harness.test({ text : 'SELECT `user`.`id` FROM `user` WHERE (((`user`.`name` = ?) OR (`user`.`name` = ?)) AND (`user`.`name` = ?))', string: 'SELECT `user`.`id` FROM `user` WHERE (((`user`.`name` = \'foo\') OR (`user`.`name` = \'baz\')) AND (`user`.`name` = \'bar\'))' }, + mssql: { + text : 'SELECT [user].[id] FROM [user] WHERE ((([user].[name] = @1) OR ([user].[name] = @2)) AND ([user].[name] = @3))', + string: 'SELECT [user].[id] FROM [user] WHERE ((([user].[name] = \'foo\') OR ([user].[name] = \'baz\')) AND ([user].[name] = \'bar\'))' + }, + oracle: { + text : 'SELECT "user"."id" FROM "user" WHERE ((("user"."name" = :1) OR ("user"."name" = :2)) AND ("user"."name" = :3))', + string: 'SELECT "user"."id" FROM "user" WHERE ((("user"."name" = \'foo\') OR ("user"."name" = \'baz\')) AND ("user"."name" = \'bar\'))' + }, params: ['foo', 'baz', 'bar'] }); @@ -153,6 +301,14 @@ Harness.test({ text : 'SELECT `user`.`id` FROM `user` WHERE (`user`.`name` IN (?, ?))', string: 'SELECT `user`.`id` FROM `user` WHERE (`user`.`name` IN (\'foo\', \'bar\'))' }, + mssql: { + text : 'SELECT [user].[id] FROM [user] WHERE ([user].[name] IN (@1, @2))', + string: 'SELECT [user].[id] FROM [user] WHERE ([user].[name] IN (\'foo\', \'bar\'))' + }, + oracle: { + text : 'SELECT "user"."id" FROM "user" WHERE ("user"."name" IN (:1, :2))', + string: 'SELECT "user"."id" FROM "user" WHERE ("user"."name" IN (\'foo\', \'bar\'))' + }, params: ['foo', 'bar'] }); @@ -170,6 +326,14 @@ Harness.test({ text : 'SELECT `user`.`id` FROM `user` WHERE ((`user`.`name` IN (?, ?)) AND (`user`.`id` = ?))', string: 'SELECT `user`.`id` FROM `user` WHERE ((`user`.`name` IN (\'foo\', \'bar\')) AND (`user`.`id` = 1))' }, + mssql: { + text : 'SELECT [user].[id] FROM [user] WHERE (([user].[name] IN (@1, @2)) AND ([user].[id] = @3))', + string: 'SELECT [user].[id] FROM [user] WHERE (([user].[name] IN (\'foo\', \'bar\')) AND ([user].[id] = 1))' + }, + oracle: { + text : 'SELECT "user"."id" FROM "user" WHERE (("user"."name" IN (:1, :2)) AND ("user"."id" = :3))', + string: 'SELECT "user"."id" FROM "user" WHERE (("user"."name" IN (\'foo\', \'bar\')) AND ("user"."id" = 1))' + }, params: ['foo', 'bar', 1] }); @@ -187,6 +351,14 @@ Harness.test({ text : 'SELECT `user`.`id`, `user`.`name` FROM `user`', string: 'SELECT `user`.`id`, `user`.`name` FROM `user`' }, + mssql: { + text : 'SELECT [user].[id], [user].[name] FROM [user]', + string: 'SELECT [user].[id], [user].[name] FROM [user]' + }, + oracle: { + text : 'SELECT "user"."id", "user"."name" FROM "user"', + string: 'SELECT "user"."id", "user"."name" FROM "user"' + }, params: [] }); @@ -211,6 +383,14 @@ Harness.test({ text : 'SELECT `user`.`id` FROM `user` WHERE (((`user`.`name` = ?) AND (`user`.`id` = ?)) OR ((`user`.`name` = ?) AND (`user`.`id` = ?)))', string: 'SELECT `user`.`id` FROM `user` WHERE (((`user`.`name` = \'boom\') AND (`user`.`id` = 1)) OR ((`user`.`name` = \'bang\') AND (`user`.`id` = 2)))' }, + mssql: { + text : 'SELECT [user].[id] FROM [user] WHERE ((([user].[name] = @1) AND ([user].[id] = @2)) OR (([user].[name] = @3) AND ([user].[id] = @4)))', + string: 'SELECT [user].[id] FROM [user] WHERE ((([user].[name] = \'boom\') AND ([user].[id] = 1)) OR (([user].[name] = \'bang\') AND ([user].[id] = 2)))' + }, + oracle: { + text : 'SELECT "user"."id" FROM "user" WHERE ((("user"."name" = :1) AND ("user"."id" = :2)) OR (("user"."name" = :3) AND ("user"."id" = :4)))', + string: 'SELECT "user"."id" FROM "user" WHERE ((("user"."name" = \'boom\') AND ("user"."id" = 1)) OR (("user"."name" = \'bang\') AND ("user"."id" = 2)))' + }, params: ['boom', 1, 'bang', 2] }); @@ -228,6 +408,14 @@ Harness.test({ text : 'SELECT `user`.`name` AS `user name`, `user`.`id` AS `user id` FROM `user`', string: 'SELECT `user`.`name` AS `user name`, `user`.`id` AS `user id` FROM `user`' }, + mssql: { + text : 'SELECT [user].[name] AS [user name], [user].[id] AS [user id] FROM [user]', + string: 'SELECT [user].[name] AS [user name], [user].[id] AS [user id] FROM [user]' + }, + oracle: { + text : 'SELECT "user"."name" "user name", "user"."id" "user id" FROM "user"', + string: 'SELECT "user"."name" "user name", "user"."id" "user id" FROM "user"' + }, params: [] }); @@ -245,6 +433,14 @@ Harness.test({ text : 'SELECT `user`.`name` AS `user name` FROM `user` WHERE (`user`.`name` = ?)', string: 'SELECT `user`.`name` AS `user name` FROM `user` WHERE (`user`.`name` = \'brian\')' }, + mssql: { + text : 'SELECT [user].[name] AS [user name] FROM [user] WHERE ([user].[name] = @1)', + string: 'SELECT [user].[name] AS [user name] FROM [user] WHERE ([user].[name] = \'brian\')' + }, + oracle: { + text : 'SELECT "user"."name" "user name" FROM "user" WHERE ("user"."name" = :1)', + string: 'SELECT "user"."name" "user name" FROM "user" WHERE ("user"."name" = \'brian\')' + }, params: ['brian'] }); @@ -262,6 +458,14 @@ Harness.test({ text : 'SELECT `user`.`name` FROM `user` WHERE (`user`.`name` = ?)', string: 'SELECT `user`.`name` FROM `user` WHERE (`user`.`name` = \'brian\')' }, + mssql: { + text : 'SELECT [user].[name] FROM [user] WHERE ([user].[name] = @1)', + string: 'SELECT [user].[name] FROM [user] WHERE ([user].[name] = \'brian\')' + }, + oracle: { + text : 'SELECT "user"."name" FROM "user" WHERE ("user"."name" = :1)', + string: 'SELECT "user"."name" FROM "user" WHERE ("user"."name" = \'brian\')' + }, params: ['brian'] }); @@ -279,6 +483,14 @@ Harness.test({ text : 'SELECT name FROM user WHERE (name <> NULL)', string: 'SELECT name FROM user WHERE (name <> NULL)' }, + mssql: { + text : 'SELECT name FROM user WHERE (name <> NULL)', + string: 'SELECT name FROM user WHERE (name <> NULL)' + }, + oracle: { + text : 'SELECT name FROM user WHERE (name <> NULL)', + string: 'SELECT name FROM user WHERE (name <> NULL)' + }, params: [] }); @@ -296,6 +508,14 @@ Harness.test({ text : 'SELECT name,id FROM user WHERE (name <> NULL)', string: 'SELECT name,id FROM user WHERE (name <> NULL)' }, + mssql: { + text : 'SELECT name,id FROM user WHERE (name <> NULL)', + string: 'SELECT name,id FROM user WHERE (name <> NULL)' + }, + oracle: { + text : 'SELECT name,id FROM user WHERE (name <> NULL)', + string: 'SELECT name,id FROM user WHERE (name <> NULL)' + }, params: [] }); @@ -313,6 +533,14 @@ Harness.test({ text : 'SELECT name, id FROM user WHERE (name <> NULL)', string: 'SELECT name, id FROM user WHERE (name <> NULL)' }, + mssql: { + text : 'SELECT name, id FROM user WHERE (name <> NULL)', + string: 'SELECT name, id FROM user WHERE (name <> NULL)' + }, + oracle: { + text : 'SELECT name, id FROM user WHERE (name <> NULL)', + string: 'SELECT name, id FROM user WHERE (name <> NULL)' + }, params: [] }); @@ -330,6 +558,14 @@ Harness.test({ text : 'SELECT name, id FROM user WHERE ((name <> NULL) AND (id <> NULL))', string: 'SELECT name, id FROM user WHERE ((name <> NULL) AND (id <> NULL))' }, + mssql: { + text : 'SELECT name, id FROM user WHERE ((name <> NULL) AND (id <> NULL))', + string: 'SELECT name, id FROM user WHERE ((name <> NULL) AND (id <> NULL))' + }, + oracle: { + text : 'SELECT name, id FROM user WHERE ((name <> NULL) AND (id <> NULL))', + string: 'SELECT name, id FROM user WHERE ((name <> NULL) AND (id <> NULL))' + }, params: [] }); @@ -349,6 +585,14 @@ Harness.test({ text : 'SELECT name FROM user WHERE (`user`.`name` = ?)', string: 'SELECT name FROM user WHERE (`user`.`name` = \'brian\')' }, + mssql: { + text : 'SELECT name FROM user WHERE ([user].[name] = @1)', + string: 'SELECT name FROM user WHERE ([user].[name] = \'brian\')' + }, + oracle: { + text : 'SELECT name FROM user WHERE ("user"."name" = :1)', + string: 'SELECT name FROM user WHERE ("user"."name" = \'brian\')' + }, params: ['brian'] }); @@ -369,6 +613,14 @@ Harness.test({ text : 'SELECT name FROM user WHERE ((`user`.`name` = ?) AND (`user`.`id` = ?))', string: 'SELECT name FROM user WHERE ((`user`.`name` = \'brian\') AND (`user`.`id` = 1))' }, + mssql: { + text : 'SELECT name FROM user WHERE (([user].[name] = @1) AND ([user].[id] = @2))', + string: 'SELECT name FROM user WHERE (([user].[name] = \'brian\') AND ([user].[id] = 1))' + }, + oracle: { + text : 'SELECT name FROM user WHERE (("user"."name" = :1) AND ("user"."id" = :2))', + string: 'SELECT name FROM user WHERE (("user"."name" = \'brian\') AND ("user"."id" = 1))' + }, params: ['brian', 1] }); @@ -386,6 +638,14 @@ Harness.test({ text : 'SELECT `user`.`name` AS `quote"quote"tick``tick``` FROM `user`', string: 'SELECT `user`.`name` AS `quote"quote"tick``tick``` FROM `user`' }, + mssql: { + text : 'SELECT [user].[name] AS [quote"quote"tick`tick`] FROM [user]', + string: 'SELECT [user].[name] AS [quote"quote"tick`tick`] FROM [user]' + }, + oracle: { + text : 'SELECT "user"."name" "quote""quote""tick`tick`" FROM "user"', + string: 'SELECT "user"."name" "quote""quote""tick`tick`" FROM "user"' + }, params: [] }); @@ -403,5 +663,13 @@ Harness.test({ text : 'SELECT `user`.* FROM `user` WHERE (`user`.`id` IN (SELECT `user`.`id` FROM `user`))', string: 'SELECT `user`.* FROM `user` WHERE (`user`.`id` IN (SELECT `user`.`id` FROM `user`))' }, + mssql: { + text : 'SELECT [user].* FROM [user] WHERE ([user].[id] IN (SELECT [user].[id] FROM [user]))', + string: 'SELECT [user].* FROM [user] WHERE ([user].[id] IN (SELECT [user].[id] FROM [user]))' + }, + oracle: { + text : 'SELECT "user".* FROM "user" WHERE ("user"."id" IN (SELECT "user"."id" FROM "user"))', + string: 'SELECT "user".* FROM "user" WHERE ("user"."id" IN (SELECT "user"."id" FROM "user"))' + }, params: [] }); diff --git a/test/dialects/ternary-clause-tests.js b/test/dialects/ternary-clause-tests.js index 37ff8a3b..04832fbc 100644 --- a/test/dialects/ternary-clause-tests.js +++ b/test/dialects/ternary-clause-tests.js @@ -3,7 +3,6 @@ var Harness = require('./support'); var customer = Harness.defineCustomerTable(); var post = Harness.definePostTable(); -var Table = require(__dirname + '/../../lib/table'); Harness.test({ query: customer.select().where(customer.age.between(18, 25)), @@ -19,6 +18,14 @@ Harness.test({ text : 'SELECT `customer`.* FROM `customer` WHERE (`customer`.`age` BETWEEN ? AND ?)', string: 'SELECT `customer`.* FROM `customer` WHERE (`customer`.`age` BETWEEN 18 AND 25)' }, + mssql: { + text : 'SELECT [customer].* FROM [customer] WHERE ([customer].[age] BETWEEN @1 AND @2)', + string: 'SELECT [customer].* FROM [customer] WHERE ([customer].[age] BETWEEN 18 AND 25)' + }, + oracle: { + text : 'SELECT "customer".* FROM "customer" WHERE ("customer"."age" BETWEEN :1 AND :2)', + string: 'SELECT "customer".* FROM "customer" WHERE ("customer"."age" BETWEEN 18 AND 25)' + }, params: [18, 25] }); @@ -36,5 +43,13 @@ Harness.test({ text : 'SELECT `post`.* FROM `post` WHERE (`post`.`userId` BETWEEN (SELECT MIN(`customer`.`id`) AS `id_min` FROM `customer`) AND (SELECT MAX(`customer`.`id`) AS `id_max` FROM `customer`))', string: 'SELECT `post`.* FROM `post` WHERE (`post`.`userId` BETWEEN (SELECT MIN(`customer`.`id`) AS `id_min` FROM `customer`) AND (SELECT MAX(`customer`.`id`) AS `id_max` FROM `customer`))' }, + mssql: { + text : 'SELECT [post].* FROM [post] WHERE ([post].[userId] BETWEEN (SELECT MIN([customer].[id]) AS [id_min] FROM [customer]) AND (SELECT MAX([customer].[id]) AS [id_max] FROM [customer]))', + string: 'SELECT [post].* FROM [post] WHERE ([post].[userId] BETWEEN (SELECT MIN([customer].[id]) AS [id_min] FROM [customer]) AND (SELECT MAX([customer].[id]) AS [id_max] FROM [customer]))' + }, + oracle: { + text : 'SELECT "post".* FROM "post" WHERE ("post"."userId" BETWEEN (SELECT MIN("customer"."id") "id_min" FROM "customer") AND (SELECT MAX("customer"."id") "id_max" FROM "customer"))', + string: 'SELECT "post".* FROM "post" WHERE ("post"."userId" BETWEEN (SELECT MIN("customer"."id") "id_min" FROM "customer") AND (SELECT MAX("customer"."id") "id_max" FROM "customer"))' + }, params: [] }); diff --git a/test/dialects/tostring-tests.js b/test/dialects/tostring-tests.js index df55dda6..53d413e8 100644 --- a/test/dialects/tostring-tests.js +++ b/test/dialects/tostring-tests.js @@ -1,9 +1,8 @@ 'use strict'; -var assert = require('assert'); -var Harness = require('./support'); -var ParameterNode = require('../../lib/node/parameter'); -var post = Harness.definePostTable(); +var assert = require('assert'); +var Harness = require('./support'); +var post = Harness.definePostTable(); // Null Harness.test({ @@ -20,6 +19,14 @@ Harness.test({ text : '(`post`.`content` = ?)', string: '(`post`.`content` = NULL)' }, + mssql: { + text : '([post].[content] = @1)', + string: '([post].[content] = NULL)' + }, + oracle: { + text : '("post"."content" = :1)', + string: '("post"."content" = NULL)' + }, params: [null] }); @@ -38,6 +45,14 @@ Harness.test({ text : '(`post`.`content` = ?)', string: '(`post`.`content` = 3.14)' }, + mssql: { + text : '([post].[content] = @1)', + string: '([post].[content] = 3.14)' + }, + oracle: { + text : '("post"."content" = :1)', + string: '("post"."content" = 3.14)' + }, params: [3.14] }); @@ -56,6 +71,14 @@ Harness.test({ text : '(`post`.`content` = ?)', string: '(`post`.`content` = \'hello\'\'\')' }, + mssql: { + text : '([post].[content] = @1)', + string: '([post].[content] = \'hello\'\'\')' + }, + oracle: { + text : '("post"."content" = :1)', + string: '("post"."content" = \'hello\'\'\')' + }, params: ['hello\''] }); @@ -74,6 +97,14 @@ Harness.test({ text : '(`post`.`content` = (?, ?, ?))', string: '(`post`.`content` = (1, \'2\', NULL))' }, + mssql: { + text : 'SQL Server does not support arrays.', + throws: true + }, + oracle: { + text : 'SQL Server does not support arrays.', + throws: true + }, params: [1, '2', null] }); @@ -92,6 +123,43 @@ Harness.test({ text : '(`post`.`content` = ?)', string: '(`post`.`content` = \'2000-01-01T00:00:00.000Z\')' }, + mssql: { + text : '([post].[content] = @1)', + string: '([post].[content] = \'2000-01-01T00:00:00.000Z\')' + }, + oracle: { + text : '("post"."content" = :1)', + string: '("post"."content" = \'2000-01-01T00:00:00.000Z\')' + }, + params: [new Date('Sat, 01 Jan 2000 00:00:00 GMT')] +}); + +// Date to milliseconds +Harness.test({ + query: post.content.equals(new Date('Sat, 01 Jan 2000 00:00:00 GMT')), + pg: { + text : '("post"."content" = $1)', + string: '("post"."content" = \'2000-01-01T00:00:00.000Z\')' + }, + sqlite: { + text : '("post"."content" = $1)', + string: '("post"."content" = 946684800000)', + config: { + dateTimeMillis: true + } + }, + mysql: { + text : '(`post`.`content` = ?)', + string: '(`post`.`content` = \'2000-01-01T00:00:00.000Z\')' + }, + mssql: { + text : '([post].[content] = @1)', + string: '([post].[content] = \'2000-01-01T00:00:00.000Z\')' + }, + oracle: { + text : '("post"."content" = :1)', + string: '("post"."content" = \'2000-01-01T00:00:00.000Z\')' + }, params: [new Date('Sat, 01 Jan 2000 00:00:00 GMT')] }); @@ -116,6 +184,14 @@ Harness.test({ text : '(`post`.`content` = ?)', string: '(`post`.`content` = \'secretMessage\')' }, + mssql: { + text : '([post].[content] = @1)', + string: '([post].[content] = \'secretMessage\')' + }, + oracle: { + text : '("post"."content" = :1)', + string: '("post"."content" = \'secretMessage\')' + }, params: [customObject] }); diff --git a/test/dialects/truncate-table-tests.js b/test/dialects/truncate-table-tests.js new file mode 100644 index 00000000..cfdb04da --- /dev/null +++ b/test/dialects/truncate-table-tests.js @@ -0,0 +1,29 @@ +'use strict'; + +var Harness = require('./support'); +var post = Harness.definePostTable(); + +Harness.test({ + query: post.truncate(), + pg: { + text : 'TRUNCATE TABLE "post"', + string: 'TRUNCATE TABLE "post"' + }, + sqlite: { + text : 'DELETE FROM "post"', + string: 'DELETE FROM "post"' + }, + mysql: { + text : 'TRUNCATE TABLE `post`', + string: 'TRUNCATE TABLE `post`' + }, + mssql: { + text : 'TRUNCATE TABLE [post]', + string: 'TRUNCATE TABLE [post]' + }, + oracle: { + text : 'TRUNCATE TABLE "post"', + string: 'TRUNCATE TABLE "post"' + }, + params: [] +}); diff --git a/test/dialects/unary-clause-tests.js b/test/dialects/unary-clause-tests.js index 8c26c624..875de382 100644 --- a/test/dialects/unary-clause-tests.js +++ b/test/dialects/unary-clause-tests.js @@ -3,7 +3,6 @@ var Harness = require('./support'); var customer = Harness.defineCustomerTable(); var post = Harness.definePostTable(); -var Table = require(__dirname + '/../../lib/table'); Harness.test({ query: customer.select().where(customer.age.isNotNull()), @@ -19,6 +18,14 @@ Harness.test({ text : 'SELECT `customer`.* FROM `customer` WHERE (`customer`.`age` IS NOT NULL)', string: 'SELECT `customer`.* FROM `customer` WHERE (`customer`.`age` IS NOT NULL)' }, + mssql: { + text : 'SELECT [customer].* FROM [customer] WHERE ([customer].[age] IS NOT NULL)', + string: 'SELECT [customer].* FROM [customer] WHERE ([customer].[age] IS NOT NULL)' + }, + oracle: { + text : 'SELECT "customer".* FROM "customer" WHERE ("customer"."age" IS NOT NULL)', + string: 'SELECT "customer".* FROM "customer" WHERE ("customer"."age" IS NOT NULL)' + }, params: [] }); @@ -36,5 +43,13 @@ Harness.test({ text : 'SELECT `post`.* FROM `post` WHERE (`post`.`userId` IN (SELECT `customer`.`id` FROM `customer` WHERE (`customer`.`age` IS NULL)))', string: 'SELECT `post`.* FROM `post` WHERE (`post`.`userId` IN (SELECT `customer`.`id` FROM `customer` WHERE (`customer`.`age` IS NULL)))' }, + mssql: { + text : 'SELECT [post].* FROM [post] WHERE ([post].[userId] IN (SELECT [customer].[id] FROM [customer] WHERE ([customer].[age] IS NULL)))', + string: 'SELECT [post].* FROM [post] WHERE ([post].[userId] IN (SELECT [customer].[id] FROM [customer] WHERE ([customer].[age] IS NULL)))' + }, + oracle: { + text : 'SELECT "post".* FROM "post" WHERE ("post"."userId" IN (SELECT "customer"."id" FROM "customer" WHERE ("customer"."age" IS NULL)))', + string: 'SELECT "post".* FROM "post" WHERE ("post"."userId" IN (SELECT "customer"."id" FROM "customer" WHERE ("customer"."age" IS NULL)))' + }, params: [] }); diff --git a/test/dialects/update-tests.js b/test/dialects/update-tests.js index bd4f6e46..eb3d0b3e 100644 --- a/test/dialects/update-tests.js +++ b/test/dialects/update-tests.js @@ -3,6 +3,7 @@ var Harness = require('./support'); var post = Harness.definePostTable(); var user = Harness.defineUserTable(); +var variable = Harness.defineVariableTable(); Harness.test({ query: post.update({ @@ -20,6 +21,14 @@ Harness.test({ text : 'UPDATE `post` SET `content` = ?', string: 'UPDATE `post` SET `content` = \'test\'' }, + mssql: { + text : 'UPDATE [post] SET [content] = @1', + string: 'UPDATE [post] SET [content] = \'test\'' + }, + oracle: { + text : 'UPDATE "post" SET "content" = :1', + string: 'UPDATE "post" SET "content" = \'test\'' + }, params: ['test'] }); @@ -40,6 +49,14 @@ Harness.test({ text : 'UPDATE `post` SET `content` = ?, `userId` = ?', string: 'UPDATE `post` SET `content` = \'test\', `userId` = 3' }, + mssql: { + text : 'UPDATE [post] SET [content] = @1, [userId] = @2', + string: 'UPDATE [post] SET [content] = \'test\', [userId] = 3' + }, + oracle: { + text : 'UPDATE "post" SET "content" = :1, "userId" = :2', + string: 'UPDATE "post" SET "content" = \'test\', "userId" = 3' + }, params: ['test', 3] }); @@ -60,6 +77,14 @@ Harness.test({ text : 'UPDATE `post` SET `content` = ?, `userId` = ?', string: 'UPDATE `post` SET `content` = NULL, `userId` = 3' }, + mssql: { + text : 'UPDATE [post] SET [content] = @1, [userId] = @2', + string: 'UPDATE [post] SET [content] = NULL, [userId] = 3' + }, + oracle: { + text : 'UPDATE "post" SET "content" = :1, "userId" = :2', + string: 'UPDATE "post" SET "content" = NULL, "userId" = 3' + }, params: [null, 3] }); @@ -80,6 +105,14 @@ Harness.test({ text : 'UPDATE `post` SET `content` = ?, `userId` = ? WHERE (`post`.`content` = ?)', string: 'UPDATE `post` SET `content` = \'test\', `userId` = 3 WHERE (`post`.`content` = \'no\')' }, + mssql: { + text : 'UPDATE [post] SET [content] = @1, [userId] = @2 WHERE ([post].[content] = @3)', + string: 'UPDATE [post] SET [content] = \'test\', [userId] = 3 WHERE ([post].[content] = \'no\')' + }, + oracle: { + text : 'UPDATE "post" SET "content" = :1, "userId" = :2 WHERE ("post"."content" = :3)', + string: 'UPDATE "post" SET "content" = \'test\', "userId" = 3 WHERE ("post"."content" = \'no\')' + }, params: ['test', 3, 'no'] }); @@ -99,6 +132,14 @@ Harness.test({ text : 'UPDATE `post` SET `content` = `user`.`name` FROM `user` WHERE (`post`.`userId` = `user`.`id`)', string: 'UPDATE `post` SET `content` = `user`.`name` FROM `user` WHERE (`post`.`userId` = `user`.`id`)' }, + mssql: { + text : 'UPDATE [post] SET [content] = [user].[name] FROM [user] WHERE ([post].[userId] = [user].[id])', + string: 'UPDATE [post] SET [content] = [user].[name] FROM [user] WHERE ([post].[userId] = [user].[id])' + }, + oracle: { + text : 'UPDATE "post" SET "content" = "user"."name" FROM "user" WHERE ("post"."userId" = "user"."id")', + string: 'UPDATE "post" SET "content" = "user"."name" FROM "user" WHERE ("post"."userId" = "user"."id")' + }, params: [] }); @@ -119,5 +160,87 @@ Harness.test({ text : 'UPDATE `post` SET `userId` = `user`.`id` FROM `user` WHERE (`post`.`userId` = `user`.`id`)', string: 'UPDATE `post` SET `userId` = `user`.`id` FROM `user` WHERE (`post`.`userId` = `user`.`id`)' }, + mssql: { + text : 'UPDATE [post] SET [userId] = [user].[id] FROM [user] WHERE ([post].[userId] = [user].[id])', + string: 'UPDATE [post] SET [userId] = [user].[id] FROM [user] WHERE ([post].[userId] = [user].[id])' + }, + oracle: { + text : 'UPDATE "post" SET "userId" = "user"."id" FROM "user" WHERE ("post"."userId" = "user"."id")', + string: 'UPDATE "post" SET "userId" = "user"."id" FROM "user" WHERE ("post"."userId" = "user"."id")' + }, params: [] }); + +// Binary updates +Harness.test({ + query: post.update({ + content: new Buffer('test') + }), + pg: { + text : 'UPDATE "post" SET "content" = $1', + string: 'UPDATE "post" SET "content" = \'\\x74657374\'' + }, + sqlite: { + text : 'UPDATE "post" SET "content" = $1', + string: 'UPDATE "post" SET "content" = x\'74657374\'' + }, + mysql: { + text : 'UPDATE `post` SET `content` = ?', + string: 'UPDATE `post` SET `content` = x\'74657374\'' + }, + oracle: { + text : 'UPDATE "post" SET "content" = :1', + string: 'UPDATE "post" SET "content" = utl_raw.cast_to_varchar2(hextoraw(\'74657374\'))' + }, + params: [new Buffer('test')] +}); + +// Boolean updates +Harness.test({ + query: variable.update({ + a: true, + b: false + }), + pg: { + text : 'UPDATE "variable" SET "a" = $1, "b" = $2', + string: 'UPDATE "variable" SET "a" = TRUE, "b" = FALSE' + }, + sqlite: { + text : 'UPDATE "variable" SET "a" = $1, "b" = $2', + string: 'UPDATE "variable" SET "a" = 1, "b" = 0' + }, + mysql: { + text : 'UPDATE `variable` SET `a` = ?, `b` = ?', + string: 'UPDATE `variable` SET `a` = TRUE, `b` = FALSE' + }, + oracle: { + text : 'UPDATE "variable" SET "a" = :1, "b" = :2', + string: 'UPDATE "variable" SET "a" = TRUE, "b" = FALSE' + }, + params: [true, false] +}); + +// Object updates +Harness.test({ + query: variable.update({ + a: {"id": 1, "value": 2}, + b: [{"id": 2, "value": 3}, {"id": 3, "value": 4}] + }), + pg: { + text : 'UPDATE "variable" SET "a" = $1, "b" = $2', + string: 'UPDATE "variable" SET "a" = \'{"id":1,"value":2}\', "b" = \'[{"id":2,"value":3},{"id":3,"value":4}]\'' + }, + sqlite: { + text : 'UPDATE "variable" SET "a" = $1, "b" = $2', + string: 'UPDATE "variable" SET "a" = \'{"id":1,"value":2}\', "b" = \'[{"id":2,"value":3},{"id":3,"value":4}]\'' + }, + mysql: { + text : 'UPDATE `variable` SET `a` = ?, `b` = ?', + string: 'UPDATE `variable` SET `a` = \'{"id":1,"value":2}\', `b` = (\'{"id":2,"value":3}\', \'{"id":3,"value":4}\')' + }, + oracle: { + text : 'UPDATE "variable" SET "a" = :1, "b" = :2', + string: 'UPDATE "variable" SET "a" = \'{"id":1,"value":2}\', "b" = (\'{"id":2,"value":3}\', \'{"id":3,"value":4}\')' + }, + params: [{"id": 1, "value": 2}, [{"id": 2, "value": 3}, {"id": 3, "value": 4}]] +}); \ No newline at end of file diff --git a/test/dialects/value-expression-tests.js b/test/dialects/value-expression-tests.js index b9e335f6..d5c7fcd4 100644 --- a/test/dialects/value-expression-tests.js +++ b/test/dialects/value-expression-tests.js @@ -2,6 +2,7 @@ var Harness = require('./support'); var customer = Harness.defineCustomerTable(); +var post = Harness.definePostTable(); var v = Harness.defineVariableTable(); // Test composition of binary methods +, *, -, =. @@ -19,6 +20,14 @@ Harness.test({ text : 'SELECT `customer`.`name`, (`customer`.`income` % ?) FROM `customer` WHERE (((`customer`.`age` + ?) * (`customer`.`age` - ?)) = ?)', string: 'SELECT `customer`.`name`, (`customer`.`income` % 100) FROM `customer` WHERE (((`customer`.`age` + 5) * (`customer`.`age` - 2)) = 10)' }, + mssql: { + text : 'SELECT [customer].[name], ([customer].[income] % @1) FROM [customer] WHERE ((([customer].[age] + @2) * ([customer].[age] - @3)) = @4)', + string: 'SELECT [customer].[name], ([customer].[income] % 100) FROM [customer] WHERE ((([customer].[age] + 5) * ([customer].[age] - 2)) = 10)' + }, + oracle: { + text : 'SELECT "customer"."name", ("customer"."income" % :1) FROM "customer" WHERE ((("customer"."age" + :2) * ("customer"."age" - :3)) = :4)', + string: 'SELECT "customer"."name", ("customer"."income" % 100) FROM "customer" WHERE ((("customer"."age" + 5) * ("customer"."age" - 2)) = 10)' + }, params: [100, 5, 2, 10] }); @@ -37,6 +46,14 @@ Harness.test({ text : 'SELECT `customer`.`name` FROM `customer` WHERE (`customer`.`name` LIKE (`customer`.`id` + ?))', string: 'SELECT `customer`.`name` FROM `customer` WHERE (`customer`.`name` LIKE (`customer`.`id` + \'hello\'))' }, + mssql: { + text : 'SELECT [customer].[name] FROM [customer] WHERE ([customer].[name] LIKE ([customer].[id] + @1))', + string: 'SELECT [customer].[name] FROM [customer] WHERE ([customer].[name] LIKE ([customer].[id] + \'hello\'))' + }, + oracle: { + text : 'SELECT "customer"."name" FROM "customer" WHERE ("customer"."name" LIKE ("customer"."id" + :1))', + string: 'SELECT "customer"."name" FROM "customer" WHERE ("customer"."name" LIKE ("customer"."id" + \'hello\'))' + }, params: ['hello'] }); @@ -56,6 +73,14 @@ Harness.test({ text : 'SELECT ((((`variable`.`a` * `variable`.`a`) / ?) + (`variable`.`v` * `variable`.`t`)) = `variable`.`d`) FROM `variable`', string: 'SELECT ((((`variable`.`a` * `variable`.`a`) / 2) + (`variable`.`v` * `variable`.`t`)) = `variable`.`d`) FROM `variable`' }, + mssql: { + text : 'SELECT (((([variable].[a] * [variable].[a]) / @1) + ([variable].[v] * [variable].[t])) = [variable].[d]) FROM [variable]', + string: 'SELECT (((([variable].[a] * [variable].[a]) / 2) + ([variable].[v] * [variable].[t])) = [variable].[d]) FROM [variable]' + }, + oracle: { + text : 'SELECT (((("variable"."a" * "variable"."a") / :1) + ("variable"."v" * "variable"."t")) = "variable"."d") FROM "variable"', + string: 'SELECT (((("variable"."a" * "variable"."a") / 2) + ("variable"."v" * "variable"."t")) = "variable"."d") FROM "variable"' + }, params: [2] }); @@ -74,5 +99,61 @@ Harness.test({ text : 'SELECT (((`variable`.`a` * `variable`.`a`) + (`variable`.`b` * `variable`.`b`)) = (`variable`.`c` * `variable`.`c`)) FROM `variable`', string: 'SELECT (((`variable`.`a` * `variable`.`a`) + (`variable`.`b` * `variable`.`b`)) = (`variable`.`c` * `variable`.`c`)) FROM `variable`' }, + mssql: { + text : 'SELECT ((([variable].[a] * [variable].[a]) + ([variable].[b] * [variable].[b])) = ([variable].[c] * [variable].[c])) FROM [variable]', + string: 'SELECT ((([variable].[a] * [variable].[a]) + ([variable].[b] * [variable].[b])) = ([variable].[c] * [variable].[c])) FROM [variable]' + }, + oracle: { + text : 'SELECT ((("variable"."a" * "variable"."a") + ("variable"."b" * "variable"."b")) = ("variable"."c" * "variable"."c")) FROM "variable"', + string: 'SELECT ((("variable"."a" * "variable"."a") + ("variable"."b" * "variable"."b")) = ("variable"."c" * "variable"."c")) FROM "variable"' + }, + params: [] +}); + +Harness.test({ + query: post.select(post.id).where(post.content.equals(new Buffer('test'))), + pg: { + text : 'SELECT "post"."id" FROM "post" WHERE ("post"."content" = $1)', + string: 'SELECT "post"."id" FROM "post" WHERE ("post"."content" = \'\\x74657374\')' + }, + sqlite: { + text : 'SELECT "post"."id" FROM "post" WHERE ("post"."content" = $1)', + string: 'SELECT "post"."id" FROM "post" WHERE ("post"."content" = x\'74657374\')' + }, + mysql: { + text : 'SELECT `post`.`id` FROM `post` WHERE (`post`.`content` = ?)', + string: 'SELECT `post`.`id` FROM `post` WHERE (`post`.`content` = x\'74657374\')' + }, + oracle: { + text : 'SELECT "post"."id" FROM "post" WHERE ("post"."content" = :1)', + string: 'SELECT "post"."id" FROM "post" WHERE ("post"."content" = utl_raw.cast_to_varchar2(hextoraw(\'74657374\')))' + }, + params: [new Buffer('test')] +}); + +// concat tests +Harness.test({ + query: post.select(post.content.concat(post.tags)), + pg: { + text : 'SELECT ("post"."content" || "post"."tags") FROM "post"', + string: 'SELECT ("post"."content" || "post"."tags") FROM "post"' + }, + sqlite: { + text : 'SELECT ("post"."content" || "post"."tags") FROM "post"', + string: 'SELECT ("post"."content" || "post"."tags") FROM "post"' + }, + mysql: { + text : 'SELECT (`post`.`content` || `post`.`tags`) FROM `post`', + string: 'SELECT (`post`.`content` || `post`.`tags`) FROM `post`' + }, + mssql: { + text : 'SELECT ([post].[content] + [post].[tags]) FROM [post]', + string: 'SELECT ([post].[content] + [post].[tags]) FROM [post]' + }, + oracle: { + text : 'SELECT ("post"."content" || "post"."tags") FROM "post"', + string: 'SELECT ("post"."content" || "post"."tags") FROM "post"' + }, params: [] }); + diff --git a/test/dialects/where-clause-tests.js b/test/dialects/where-clause-tests.js index 7fef43f2..3282b7d9 100644 --- a/test/dialects/where-clause-tests.js +++ b/test/dialects/where-clause-tests.js @@ -17,6 +17,14 @@ Harness.test({ text : 'SELECT * FROM "user" WHERE (("user"."id" IS NOT NULL) AND ("user"."name" IS NOT NULL))', string: 'SELECT * FROM "user" WHERE (("user"."id" IS NOT NULL) AND ("user"."name" IS NOT NULL))' }, + mssql: { + text : 'SELECT * FROM [user] WHERE (([user].[id] IS NOT NULL) AND ([user].[name] IS NOT NULL))', + string: 'SELECT * FROM [user] WHERE (([user].[id] IS NOT NULL) AND ([user].[name] IS NOT NULL))' + }, + oracle: { + text : 'SELECT * FROM "user" WHERE (("user"."id" IS NOT NULL) AND ("user"."name" IS NOT NULL))', + string: 'SELECT * FROM "user" WHERE (("user"."id" IS NOT NULL) AND ("user"."name" IS NOT NULL))' + }, params: [] }); @@ -34,5 +42,164 @@ Harness.test({ text : 'SELECT * FROM "user" WHERE (("user"."id" IS NOT NULL) AND ("user"."name" IS NOT NULL))', string: 'SELECT * FROM "user" WHERE (("user"."id" IS NOT NULL) AND ("user"."name" IS NOT NULL))' }, + mssql: { + text : 'SELECT * FROM [user] WHERE (([user].[id] IS NOT NULL) AND ([user].[name] IS NOT NULL))', + string: 'SELECT * FROM [user] WHERE (([user].[id] IS NOT NULL) AND ([user].[name] IS NOT NULL))' + }, + oracle: { + text : 'SELECT * FROM "user" WHERE (("user"."id" IS NOT NULL) AND ("user"."name" IS NOT NULL))', + string: 'SELECT * FROM "user" WHERE (("user"."id" IS NOT NULL) AND ("user"."name" IS NOT NULL))' + }, params: [] }); + +Harness.test({ + query: user.where([user.id.isNotNull(), user.name.isNotNull()]), + pg: { + text : 'SELECT * FROM "user" WHERE (("user"."id" IS NOT NULL) AND ("user"."name" IS NOT NULL))', + string: 'SELECT * FROM "user" WHERE (("user"."id" IS NOT NULL) AND ("user"."name" IS NOT NULL))' + }, + mysql: { + text : 'SELECT * FROM `user` WHERE ((`user`.`id` IS NOT NULL) AND (`user`.`name` IS NOT NULL))', + string: 'SELECT * FROM `user` WHERE ((`user`.`id` IS NOT NULL) AND (`user`.`name` IS NOT NULL))' + }, + sqlite: { + text : 'SELECT * FROM "user" WHERE (("user"."id" IS NOT NULL) AND ("user"."name" IS NOT NULL))', + string: 'SELECT * FROM "user" WHERE (("user"."id" IS NOT NULL) AND ("user"."name" IS NOT NULL))' + }, + mssql: { + text : 'SELECT * FROM [user] WHERE (([user].[id] IS NOT NULL) AND ([user].[name] IS NOT NULL))', + string: 'SELECT * FROM [user] WHERE (([user].[id] IS NOT NULL) AND ([user].[name] IS NOT NULL))' + }, + oracle: { + text : 'SELECT * FROM "user" WHERE (("user"."id" IS NOT NULL) AND ("user"."name" IS NOT NULL))', + string: 'SELECT * FROM "user" WHERE (("user"."id" IS NOT NULL) AND ("user"."name" IS NOT NULL))' + }, + params: [] +}); + +Harness.test({ + query: user.where([]), + pg: { + text : 'SELECT * FROM "user" WHERE (1 = 1)', + string: 'SELECT * FROM "user" WHERE (1 = 1)' + }, + mysql: { + text : 'SELECT * FROM `user` WHERE (1 = 1)', + string: 'SELECT * FROM `user` WHERE (1 = 1)' + }, + sqlite: { + text : 'SELECT * FROM "user" WHERE (1 = 1)', + string: 'SELECT * FROM "user" WHERE (1 = 1)' + }, + mssql: { + text : 'SELECT * FROM [user] WHERE (1 = 1)', + string: 'SELECT * FROM [user] WHERE (1 = 1)' + }, + oracle: { + text : 'SELECT * FROM "user" WHERE (1 = 1)', + string: 'SELECT * FROM "user" WHERE (1 = 1)' + }, + params: [] +}); + +Harness.test({ + query: user.select().where(user.id.equals(1)).and(user.name.equals('a')), + pg: { + text : 'SELECT "user".* FROM "user" WHERE (("user"."id" = $1) AND ("user"."name" = $2))', + string: 'SELECT "user".* FROM "user" WHERE (("user"."id" = 1) AND ("user"."name" = \'a\'))' + }, + mysql: { + text : 'SELECT `user`.* FROM `user` WHERE ((`user`.`id` = ?) AND (`user`.`name` = ?))', + string: 'SELECT `user`.* FROM `user` WHERE ((`user`.`id` = 1) AND (`user`.`name` = \'a\'))' + }, + sqlite: { + text : 'SELECT "user".* FROM "user" WHERE (("user"."id" = $1) AND ("user"."name" = $2))', + string: 'SELECT "user".* FROM "user" WHERE (("user"."id" = 1) AND ("user"."name" = \'a\'))' + }, + mssql: { + text : 'SELECT [user].* FROM [user] WHERE (([user].[id] = @1) AND ([user].[name] = @2))', + string: 'SELECT [user].* FROM [user] WHERE (([user].[id] = 1) AND ([user].[name] = \'a\'))' + }, + oracle: { + text : 'SELECT "user".* FROM "user" WHERE (("user"."id" = :1) AND ("user"."name" = :2))', + string: 'SELECT "user".* FROM "user" WHERE (("user"."id" = 1) AND ("user"."name" = \'a\'))' + }, + params: [1,'a'] +}); + +Harness.test({ + query: user.select().and(user.id.equals(1)), + pg: { + text : 'SELECT "user".* FROM "user" WHERE ("user"."id" = $1)', + string: 'SELECT "user".* FROM "user" WHERE ("user"."id" = 1)' + }, + mysql: { + text : 'SELECT `user`.* FROM `user` WHERE (`user`.`id` = ?)', + string: 'SELECT `user`.* FROM `user` WHERE (`user`.`id` = 1)' + }, + sqlite: { + text : 'SELECT "user".* FROM "user" WHERE ("user"."id" = $1)', + string: 'SELECT "user".* FROM "user" WHERE ("user"."id" = 1)' + }, + mssql: { + text : 'SELECT [user].* FROM [user] WHERE ([user].[id] = @1)', + string: 'SELECT [user].* FROM [user] WHERE ([user].[id] = 1)' + }, + oracle: { + text : 'SELECT "user".* FROM "user" WHERE ("user"."id" = :1)', + string: 'SELECT "user".* FROM "user" WHERE ("user"."id" = 1)' + }, + params: [1] +}); + +Harness.test({ + query: user.select().or(user.id.equals(1)), + pg: { + text : 'SELECT "user".* FROM "user" WHERE ("user"."id" = $1)', + string: 'SELECT "user".* FROM "user" WHERE ("user"."id" = 1)' + }, + mysql: { + text : 'SELECT `user`.* FROM `user` WHERE (`user`.`id` = ?)', + string: 'SELECT `user`.* FROM `user` WHERE (`user`.`id` = 1)' + }, + sqlite: { + text : 'SELECT "user".* FROM "user" WHERE ("user"."id" = $1)', + string: 'SELECT "user".* FROM "user" WHERE ("user"."id" = 1)' + }, + mssql: { + text : 'SELECT [user].* FROM [user] WHERE ([user].[id] = @1)', + string: 'SELECT [user].* FROM [user] WHERE ([user].[id] = 1)' + }, + oracle: { + text : 'SELECT "user".* FROM "user" WHERE ("user"."id" = :1)', + string: 'SELECT "user".* FROM "user" WHERE ("user"."id" = 1)' + }, + params: [1] +}); + +Harness.test({ + query: user.select().and(user.id.equals(1)).or(user.name.equals('a')), + pg: { + text : 'SELECT "user".* FROM "user" WHERE (("user"."id" = $1) OR ("user"."name" = $2))', + string: 'SELECT "user".* FROM "user" WHERE (("user"."id" = 1) OR ("user"."name" = \'a\'))' + }, + mysql: { + text : 'SELECT `user`.* FROM `user` WHERE ((`user`.`id` = ?) OR (`user`.`name` = ?))', + string: 'SELECT `user`.* FROM `user` WHERE ((`user`.`id` = 1) OR (`user`.`name` = \'a\'))' + }, + sqlite: { + text : 'SELECT "user".* FROM "user" WHERE (("user"."id" = $1) OR ("user"."name" = $2))', + string: 'SELECT "user".* FROM "user" WHERE (("user"."id" = 1) OR ("user"."name" = \'a\'))' + }, + mssql: { + text : 'SELECT [user].* FROM [user] WHERE (([user].[id] = @1) OR ([user].[name] = @2))', + string: 'SELECT [user].* FROM [user] WHERE (([user].[id] = 1) OR ([user].[name] = \'a\'))' + }, + oracle: { + text : 'SELECT "user".* FROM "user" WHERE (("user"."id" = :1) OR ("user"."name" = :2))', + string: 'SELECT "user".* FROM "user" WHERE (("user"."id" = 1) OR ("user"."name" = \'a\'))' + }, + params: [1,'a'] +}); + diff --git a/test/function-tests.js b/test/function-tests.js index 3cac3a9b..6c53072d 100644 --- a/test/function-tests.js +++ b/test/function-tests.js @@ -1,4 +1,3 @@ -/* global suite, test */ 'use strict'; var assert = require('assert'); @@ -6,7 +5,12 @@ var sql = require(__dirname + '/../lib').setDialect('postgres'); var user = sql.define({ name: 'user', - columns: ['id', 'email', 'name'] + columns: [ + {name: 'id'}, + {name:'email'}, + {name: 'name'}, + {name: 'age', property: 'howOld'} + ] }); suite('function', function() { @@ -17,6 +21,14 @@ suite('function', function() { assert.equal(aliasedUpper.text, 'UPPER("user"."email") AS "upperAlias"'); }); + test('function call on aliased column', function() { + var round = sql.functions.ROUND; + var aliasedRound = round(user.howOld, 2).toQuery(); + + assert.equal(aliasedRound.text, 'ROUND("user"."age", $1)'); + assert.equal(aliasedRound.values[0], 2); + }); + test('creating function call works', function() { var upper = sql.functionCallCreator('UPPER'); var functionCall = upper('hello', 'world').toQuery(); @@ -72,4 +84,10 @@ suite('function', function() { assert.equal(query.text, 'SELECT (AVG((DISTINCT((COUNT("user"."id") + MAX("user"."id"))) - MIN("user"."id"))) * $1) FROM "user"'); assert.equal(query.values[0], 100); }); + + test('use custom function', function() { + var query = user.select(sql.function('PHRASE_TO_TSQUERY')('simple', user.name)).toQuery(); + assert.equal(query.text, 'SELECT PHRASE_TO_TSQUERY($1, "user"."name") FROM "user"'); + assert.equal(query.values[0], 'simple'); + }); }); diff --git a/test/index-tests.js b/test/index-tests.js index 614aee95..f25839e1 100644 --- a/test/index-tests.js +++ b/test/index-tests.js @@ -1,4 +1,3 @@ -/* global suite, test */ 'use strict'; var assert = require('assert'); @@ -16,6 +15,27 @@ suite('index', function() { }); }); + test('stores the default dialect\'s name if none has been passed', function() { + assert.equal(sql.create().dialectName, 'postgres'); + }); + + test('stores the sqlite dialect', function() { + assert.equal(sql.create('sqlite').dialectName, 'sqlite'); + }); + + test('stores the mysql dialect', function() { + assert.equal(sql.create('mysql').dialectName, 'mysql'); + }); + + test('stores the mssql dialect', function() { + assert.equal(sql.create('mssql').dialectName, 'mssql'); + }); + + test('stores the oracle dialect', function() { + assert.equal(sql.create('oracle').dialectName, 'oracle'); + }); + + test('can create a query using the default dialect', function() { var query = sql.select(user.id).from(user).where(user.email.equals('brian.m.carlson@gmail.com')).toQuery(); assert.equal(query.text, 'SELECT "user"."id" FROM "user" WHERE ("user"."email" = $1)'); @@ -30,47 +50,60 @@ suite('index', function() { }); test('sql.create creates an instance with a new dialect', function() { - var mysql = sql.create('mysql'); - var query = mysql.select(user.id).from(user).where(user.email.equals('brian.m.carlson@gmail.com')).toQuery(); - assert.equal(query.text, 'SELECT `user`.`id` FROM `user` WHERE (`user`.`email` = ?)'); - assert.equal(query.values[0], 'brian.m.carlson@gmail.com'); - + var mysql = sql.create('mysql'); + var query = mysql.select(user.id).from(user).where(user.email.equals('brian.m.carlson@gmail.com')).toQuery(); + assert.equal(query.text, 'SELECT `user`.`id` FROM `user` WHERE (`user`.`email` = ?)'); + assert.equal(query.values[0], 'brian.m.carlson@gmail.com'); }); test('sql.define for parallel dialects work independently', function() { + var mssql = sql.create('mssql'); var mysql = sql.create('mysql'); var postgres = sql.create('postgres'); var sqlite = sql.create('sqlite'); + var oracle = sql.create('oracle'); + var mssqlTable = mssql.define({name: 'table', columns: ['column']}); var mysqlTable = mysql.define({name: 'table', columns: ['column']}); var postgresTable = postgres.define({name: 'table', columns: ['column']}); var sqliteTable = sqlite.define({name: 'table', columns: ['column']}); + var oracleTable = oracle.define({name: 'table', columns: ['column']}); assert.equal(mysqlTable.sql, mysql); assert.equal(postgresTable.sql, postgres); assert.equal(sqliteTable.sql, sqlite); + assert.equal(mssqlTable.sql, mssql); + assert.equal(oracleTable.sql, oracle); }); test('using Sql as a class', function() { var Sql = sql.Sql; + var mssql = new Sql('mssql'); var mysql = new Sql('mysql'); var postgres = new Sql('postgres'); var sqlite = new Sql('sqlite'); + var oracle = new Sql('oracle'); assert.equal(mysql.dialect, require(__dirname + '/../lib/dialect/mysql')); assert.equal(postgres.dialect, require(__dirname + '/../lib/dialect/postgres')); assert.equal(sqlite.dialect, require(__dirname + '/../lib/dialect/sqlite')); + assert.equal(mssql.dialect, require(__dirname + '/../lib/dialect/mssql')); + assert.equal(oracle.dialect, require(__dirname + '/../lib/dialect/oracle')); }); test('override dialect for toQuery using dialect name', function() { var Sql = sql.Sql; + var mssql = new Sql('mssql'); var mysql = new Sql('mysql'); var postgres = new Sql('postgres'); var sqlite = new Sql('sqlite'); + var oracle = new Sql('oracle'); var sqliteQuery = mysql.select(user.id).from(user).where(user.email.equals('brian.m.carlson@gmail.com')).toQuery('sqlite'); var postgresQuery = sqlite.select(user.id).from(user).where(user.email.equals('brian.m.carlson@gmail.com')).toQuery('postgres'); var mysqlQuery = postgres.select(user.id).from(user).where(user.email.equals('brian.m.carlson@gmail.com')).toQuery('mysql'); + var mssqlQuery = mysql.select(user.id).from(user).where(user.email.equals('brian.m.carlson@gmail.com')).toQuery('mssql'); + var oracleQuery = oracle.select(user.id).from(user).where(user.email.equals('brian.m.carlson@gmail.com')).toQuery('oracle'); var values = ['brian.m.carlson@gmail.com']; assert.equal(sqliteQuery.text, 'SELECT "user"."id" FROM "user" WHERE ("user"."email" = $1)'); @@ -81,6 +114,12 @@ suite('index', function() { assert.equal(mysqlQuery.text, 'SELECT `user`.`id` FROM `user` WHERE (`user`.`email` = ?)'); assert.deepEqual(mysqlQuery.values, values); + + assert.equal(mssqlQuery.text, 'SELECT [user].[id] FROM [user] WHERE ([user].[email] = @1)'); + assert.deepEqual(mssqlQuery.values, values); + + assert.equal(oracleQuery.text, 'SELECT "user"."id" FROM "user" WHERE ("user"."email" = :1)'); + assert.deepEqual(oracleQuery.values, values); }); test('override dialect for toQuery using invalid dialect name', function() { @@ -89,4 +128,87 @@ suite('index', function() { query.toQuery('invalid'); }); }); + + test('using named queries with toNamedQuery', function() { + var query = sql.select(user.id).from(user).where(user.email.equals('brian.m.carlson@gmail.com')).toNamedQuery('users'); + assert.equal(query.text, 'SELECT "user"."id" FROM "user" WHERE ("user"."email" = $1)'); + assert.equal(query.values[0], 'brian.m.carlson@gmail.com'); + assert.equal(query.name, 'users'); + }); + + test('provide an empty query name for toNamedQuery', function() { + var query = sql.select(user.id).from(user); + assert.throws(function() { + query.toNamedQuery(''); + }); + }); + + test('provide an undefined query name for toNamedQuery', function() { + var query = sql.select(user.id).from(user); + assert.throws(function() { + query.toNamedQuery(); + }); + }); + + test('override dialect for toNamedQuery using dialect name', function() { + var Sql = sql.Sql; + var mysql = new Sql('mysql'); + var postgres = new Sql('postgres'); + var sqlite = new Sql('sqlite'); + var mssql = new Sql('mssql'); + var oracle = new Sql('oracle'); + + var sqliteQuery = mysql.select(user.id).from(user).where(user.email.equals('brian.m.carlson@gmail.com')).toNamedQuery('user.select_brian','sqlite'); + var postgresQuery = sqlite.select(user.id).from(user).where(user.email.equals('brian.m.carlson@gmail.com')).toNamedQuery('user.select_brian','postgres'); + var mysqlQuery = postgres.select(user.id).from(user).where(user.email.equals('brian.m.carlson@gmail.com')).toNamedQuery('user.select_brian','mysql'); + var oracleQuery = mssql.select(user.id).from(user).where(user.email.equals('brian.m.carlson@gmail.com')).toNamedQuery('user.select_brian','oracle'); + var mssqlQuery = oracle.select(user.id).from(user).where(user.email.equals('brian.m.carlson@gmail.com')).toNamedQuery('user.select_brian','mssql'); + + + var values = ['brian.m.carlson@gmail.com']; + assert.equal(sqliteQuery.text, 'SELECT "user"."id" FROM "user" WHERE ("user"."email" = $1)'); + assert.deepEqual(sqliteQuery.values, values); + assert.equal('user.select_brian', sqliteQuery.name); + + assert.equal(postgresQuery.text, 'SELECT "user"."id" FROM "user" WHERE ("user"."email" = $1)'); + assert.deepEqual(postgresQuery.values, values); + assert.equal('user.select_brian', postgresQuery.name); + + assert.equal(mysqlQuery.text, 'SELECT `user`.`id` FROM `user` WHERE (`user`.`email` = ?)'); + assert.deepEqual(mysqlQuery.values, values); + assert.equal('user.select_brian', mysqlQuery.name); + + assert.equal(mssqlQuery.text, 'SELECT [user].[id] FROM [user] WHERE ([user].[email] = @1)'); + assert.deepEqual(mssqlQuery.values, values); + assert.equal('user.select_brian', mssqlQuery.name); + + assert.equal(oracleQuery.text, 'SELECT "user"."id" FROM "user" WHERE ("user"."email" = :1)'); + assert.deepEqual(oracleQuery.values, values); + assert.equal('user.select_brian', oracleQuery.name); + + }); + + test('override dialect for toNamedQuery using invalid dialect name', function() { + var query = sql.select(user.id).from(user); + assert.throws(function() { + query.toNamedQuery('name', 'invalid'); + }); + }); + + test('mssql default parameter place holder is @index', function() { + var Sql = sql.Sql; + var mssql = new Sql('mssql'); + var query = mssql.select(user.id).from(user).where(user.email.equals('x@y.com')).toQuery(); + assert.equal(query.text, 'SELECT [user].[id] FROM [user] WHERE ([user].[email] = @1)'); + assert.equal(query.values[0], 'x@y.com'); + }); + + test('mssql override default parameter placeholder with ?', function() { + var Sql = sql.Sql; + var mssql = new Sql('mssql',{questionMarkParameterPlaceholder:true}); + var query = mssql.select(user.id).from(user).where(user.email.equals('x@y.com')).toQuery(); + assert.equal(query.text, 'SELECT [user].[id] FROM [user] WHERE ([user].[email] = ?)'); + assert.equal(query.values[0], 'x@y.com'); + }); + }); diff --git a/test/mocha.opts b/test/mocha.opts index 5efaf24d..850a6c2f 100644 --- a/test/mocha.opts +++ b/test/mocha.opts @@ -1 +1,2 @@ +--reporter dot --ui tdd diff --git a/test/select-tests.js b/test/select-tests.js index af8db6d2..a0befc4c 100644 --- a/test/select-tests.js +++ b/test/select-tests.js @@ -1,4 +1,3 @@ -/* global test */ 'use strict'; var assert = require('assert'); diff --git a/test/table-tests.js b/test/table-tests.js index 53401fb2..73d43367 100644 --- a/test/table-tests.js +++ b/test/table-tests.js @@ -1,9 +1,9 @@ -/* global suite, test */ 'use strict'; var assert = require('assert'); var Table = require(__dirname + '/../lib/table'); var Column = require(__dirname + '/../lib/column'); +var Sql = require('../'); suite('table', function() { var table = new Table({ @@ -148,8 +148,6 @@ test('table with dynamic column definition', function() { table.addColumn('foo'); assert.equal(table.columns.length, 1); - var error = null; - assert.throws(function() { table.addColumn('foo'); }); @@ -169,6 +167,19 @@ test('hasColumn', function() { assert.equal(table.hasColumn('baz'), true); }); +test('hasColumn with user-defined column property', function() { + var table = Table.define({ + name: 'blah', + columns: [{ + name: 'id', + property: 'theId' + }, {name: 'foo'}] + }); + + assert.equal(table.hasColumn('id'), true); + assert.equal(table.hasColumn('theId'), true); +}); + test('the column "from" does not overwrite the from method', function() { var table = Table.define({ name: 'foo', columns: [] }); table.addColumn('from'); @@ -181,3 +192,82 @@ test('getColumn returns the from column', function() { assert(table.getColumn('from') instanceof Column); assert(table.get('from') instanceof Column); }); + +test('set and get schema', function () { + var table = Table.define({ name: 'foo', schema: 'bar', columns: [] }); + assert.equal(table.getSchema(), 'bar'); + table.setSchema('barbarz'); + assert.equal(table.getSchema(), 'barbarz'); +}); + +suite('table.clone', function() { + test('check if it is a copy, not just a reference', function() { + var table = Table.define({ name: 'foo', columns: [] }); + var copy = table.clone(); + assert.notEqual(table, copy); + }); + + test('copy columns', function() { + var table = Table.define({ name: 'foo', columns: ['bar'] }); + var copy = table.clone(); + assert(copy.get('bar') instanceof Column); + }); + + test('overwrite config while copying', function() { + var table = Table.define({ + name: 'foo', + schema: 'foobar', + columns: ['bar'], + snakeToCamel: true, + columnWhiteList: true + }); + + var copy = table.clone({ + schema: 'test', + snakeToCamel: false, + columnWhiteList: false + }); + + assert.equal(copy.getSchema(), 'test'); + assert.equal(copy.snakeToCamel, false); + assert.equal(copy.columnWhiteList, false); + }); +}); + +test('dialects', function () { + var sql = new Sql.Sql('mysql'); + var foo = sql.define({ name: 'foo', columns: [ 'id' ] }), + bar = sql.define({ name: 'bar', columns: [ 'id' ] }); + + var actual = foo.join(bar).on(bar.id.equals(1)).toString(); + assert.equal(actual, '`foo` INNER JOIN `bar` ON (`bar`.`id` = 1)'); + + sql = new Sql.Sql('postgres'); + foo = sql.define({ name: 'foo', columns: [ 'id' ] }); + bar = sql.define({ name: 'bar', columns: [ 'id' ] }); + actual = foo.join(bar).on(bar.id.equals(1)).toString(); + assert.equal(actual, '"foo" INNER JOIN "bar" ON ("bar"."id" = 1)'); +}); + +test('limit', function () { + var user = Table.define({name: 'user', columns: ['id', 'name']}); + var query = user.limit(3); + assert.equal(query.nodes.length, 1); + assert.equal(query.nodes[0].type, 'LIMIT'); + assert.equal(query.nodes[0].count, 3); +}); + +test('offset', function () { + var user = Table.define({name: 'user', columns: ['id', 'name']}); + var query = user.offset(20); + assert.equal(query.nodes.length, 1); + assert.equal(query.nodes[0].type, 'OFFSET'); + assert.equal(query.nodes[0].count, 20); +}); + +test('order', function () { + var user = Table.define({name: 'user', columns: ['id', 'name']}); + var query = user.order(user.name); + assert.equal(query.nodes.length, 1); + assert.equal(query.nodes[0].type, 'ORDER BY'); +}); diff --git a/test/ternary-clause-tests.js b/test/ternary-clause-tests.js index 71d897c3..1e7c975f 100644 --- a/test/ternary-clause-tests.js +++ b/test/ternary-clause-tests.js @@ -1,4 +1,3 @@ -/* global test */ 'use strict'; var assert = require('assert'); diff --git a/test/unary-clause-tests.js b/test/unary-clause-tests.js index 745eb20f..f5b36a1b 100644 --- a/test/unary-clause-tests.js +++ b/test/unary-clause-tests.js @@ -1,4 +1,3 @@ -/* global test */ 'use strict'; var assert = require('assert'); diff --git a/test/value-expression-tests.js b/test/value-expression-tests.js index 4ef2b7e0..ec80415a 100644 --- a/test/value-expression-tests.js +++ b/test/value-expression-tests.js @@ -1,4 +1,3 @@ -/* global suite, test */ 'use strict'; var assert = require('assert'); @@ -11,7 +10,9 @@ suite('value-expression', function() { // make sure that the node class doesn't have any conflicting properties for (var key in mixin) { - assert.equal(Node.prototype[key], undefined); + if (mixin.hasOwnProperty(key)) { + assert.equal(Node.prototype[key], undefined); + } } }); });