diff --git a/.gitignore b/.gitignore index c2658d7d..9cf6fffa 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ node_modules/ +.idea diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 00000000..a8e1e5f9 --- /dev/null +++ b/.jshintrc @@ -0,0 +1,10 @@ +{ + "node": true, + "globals": { + "describe" : true, + "it" : true, + "Node" : true, + "suite" : true, + "test" : true + } +} diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..07becfb2 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,11 @@ +language: node_js +node_js: + - "5.0" + - "4.2" + - "0.10" + - "0.11" + +matrix: + allow_failures: + - node_js: "0.10" + - node_js: "0.11" \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..722cf041 --- /dev/null +++ b/LICENSE @@ -0,0 +1,7 @@ +Copyright (C) 2011 Brian Carlson + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..4d56d98a --- /dev/null +++ b/Makefile @@ -0,0 +1,17 @@ +.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 33950766..e1a3af8c 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,187 @@ # node-sql -_sql string builder for node_ +_sql string builder for node_ - supports PostgreSQL, mysql, Microsoft SQL Server, Oracle and sqlite dialects. -## examples +Building SQL statements by hand is no fun, especially in a language which has clumsy support for multi-line strings. + +So let's build it with JavaScript. + +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) + +## install + +```sh +$ npm install sql +``` + +## use ```js //require the module var sql = require('sql'); -//first we define our table +//(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', + columns: ['id', 'name', 'email', 'lastLogin'] +}); + +var post = sql.define({ + name: 'post', + columns: ['id', 'userId', 'date', 'title', 'body'] +}); + +//now let's make a simple query +var query = user.select(user.star()).from(user).toQuery(); +console.log(query.text); //SELECT "user".* FROM "user" + +//something more interesting +var query = user + .select(user.id) + .from(user) + .where( + user.name.equals('boom').and(user.id.equals(1)) + ).or( + user.name.equals('bang').and(user.id.equals(2)) + ).toQuery(); + +//query is parameterized by default +console.log(query.text); //SELECT "user"."id" FROM "user" WHERE ((("user"."name" = $1) AND ("user"."id" = $2)) OR (("user"."name" = $3) AND ("user"."id" = $4))) + +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) + .from(user.join(post).on(user.id.equals(post.userId))).toQuery(); + +console.log(query.text); //'SELECT "user"."name", "post"."body" FROM "user" INNER JOIN "post" ON ("user"."id" = "post"."userId")' + +//this also makes parts of your queries composable, which is handy + +var friendship = sql.define({ + name: 'friendship', + columns: ['userId', 'friendId'] +}); + +var friends = user.as('friends'); +var userToFriends = user + .leftJoin(friendship).on(user.id.equals(friendship.userId)) + .leftJoin(friends).on(friendship.friendId.equals(friends.id)); + +//and now...compose... +var friendsWhoHaveLoggedInQuery = user.from(userToFriends).where(friends.lastLogin.isNotNull()); +//SELECT * FROM "user" +//LEFT JOIN "friendship" ON ("user"."id" = "friendship"."userId") +//LEFT JOIN "user" AS "friends" ON ("friendship"."friendId" = "friends"."id") +//WHERE "friends"."lastLogin" IS NOT NULL + +var friendsWhoUseGmailQuery = user.from(userToFriends).where(friends.email.like('%@gmail.com')); +//SELECT * FROM "user" +//LEFT JOIN "friendship" ON ("user"."id" = "friendship"."userId") +//LEFT JOIN "user" AS "friends" ON ("friendship"."friendId" = "friends"."id") +//WHERE "friends"."email" LIKE %1 + +//Using different property names for columns +//helpful if your column name is long or not camelCase var user = sql.define({ name: 'user', - columns: ['id', 'email', 'lastLogin'] + columns: [{ + name: 'id' + }, { + name: 'state_or_province', + property: 'state' + } + ] +}); + +//now, instead of user.state_or_province, you can just use user.state +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](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) +to automatically generate definition files from a database instance. For example, +running `node-sql-generate --dsn "mysql://user:password@host/database"` will generate +something similar to: + +```javascript +// autogenerated by node-sql-generate v0.0.1 on Tue May 21 2013 01:04:12 GMT-0700 (PDT) +var sql = require('sql'); + +/** + * SQL definition for database.bar + */ +exports.bar = sql.define({ + name: 'bar', + columns: [ + 'id', + 'foo_id' + ] +}); + +/** + * SQL definition for database.foo + */ +exports.foo = sql.define({ + name: 'foo', + columns: [ + 'id', + 'field_1', + 'foo_bar_baz' + ] }); -//now we make a query -var usersWhoLoggedInBeforeToday = user.select(user.id, user.email).from(user).where(user.lastLogin.lt(new Date())); -console.log(usersWhoLoggedInBeforeToday); -// { text: 'SELECT user.'id', user.'email' FROM user WHERE user.'lastLogin' > $1', values: ['2011-01-1'] } +/** + * Adding a column to an existing table: + */ +var model = sql.define({ name: 'foo', columns: [] }); +model.addColumn('id'); + +// If you try to add another column "id", node-sql will throw an error. +// You can suppress that error via: +model.addColumn('id', { noisy: false }); ``` -I know this is a cop-out for now, but for more in-depth examples view `test/dialect-tests.js` +Read the module's documentation for more details. + +## contributing + +We __love__ contributions. + +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 +2. `git pull https://github.com/(your_username)/node-sql` +3. `cd node-sql` +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. +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. + +__As long as your pull request doesn't have completely off-the-wall changes and it does have tests we will almost always merge it and push it to npm__ + +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. + +After all, open source belongs to everyone. -## help! -I need help with a mysql and sqlite syntax. Even if you don't want to contribute code, I could still use some failing tests I can work towards. You see, I don't really know mysql or sqlite syntax very well and don't use either database much. If you'd like to contribute, please message me on github. I'll give you commit access, and we'll be off to the races. +## license +MIT diff --git a/lib/column.js b/lib/column.js index 2dc6a8fb..55a3da18 100644 --- a/lib/column.js +++ b/lib/column.js @@ -1,68 +1,102 @@ -var ColumnNode = require(__dirname + '/node/column'); -var ParameterNode = require(__dirname + '/node/parameter'); -var BinaryNode = require(__dirname + '/node/binary'); -var UnaryNode = require(__dirname + '/node/unary'); -var TextNode = require(__dirname + '/node/text'); +'use strict'; + +var _ = require('lodash'); +var ColumnNode = require('./node/column'); +var OrderByValueNode = require('./node/orderByValue'); +var TextNode = require('./node/text'); +var valueExpressionMixin = require('./node/valueExpression'); + var Column = function(config) { this.table = config.table; - this.name = config.name; - if(typeof config.quote === 'undefined') { - //auto-quote if column name is upper & lower case - this.quote = this.name.toLowerCase() != this.name; - } else { - this.quote = config.quote; + for (var name in config) { + if (config.hasOwnProperty(name)) { + this[name] = config[name]; + } } this.asc = this.ascending = this; - this.desc = this.descending = new BinaryNode({ - left: this.toNode(), - right: new TextNode('DESC'), - operator: '' + this.alias = null; + this.desc = this.descending = new OrderByValueNode({ + value : this.toNode(), + direction : new TextNode('DESC') }); -} - -var binaryMethod = function(name, operator) { - Column.prototype[name] = function(val) { - return new BinaryNode({ - left: this.toNode(), - operator: operator, - right: val.toNode ? val.toNode() : new ParameterNode(val) - }) - } -} - -var unaryMethod = function(name, operator) { - Column.prototype[name] = function(val) { - return new UnaryNode({ - left: this.toNode(), - operator: operator - }); - } -} + this.dataType = config.dataType; + this.defaultValue = config.defaultValue; +}; + +// mix in value expression +_.extend(Column.prototype, valueExpressionMixin()); + +var contextify = function(base) { + var context = Object.create(Column.prototype); + Object.keys(base).forEach(function (key) { + context[key] = base[key]; + }); + return context; +}; Column.prototype.value = function(value) { - this._value = value; - return this; -} + var context = contextify(this); + context._value = value; + return context; +}; Column.prototype.getValue = function() { return this._value; -} +}; Column.prototype.toNode = function() { - //creates a query node from this column - return new ColumnNode(this); -} - -binaryMethod('equals', '='); -binaryMethod('equal', '='); -binaryMethod('notEqual', '<>'); -binaryMethod('notEquals', '<>'); -unaryMethod('isNull', 'IS NULL'); -unaryMethod('isNotNull', 'IS NOT NULL'); -binaryMethod('gt', '>'); -binaryMethod('gte', '>='); -binaryMethod('lt', '<'); -binaryMethod('lte', '<='); -binaryMethod('like', 'LIKE'); + // creates a query node from this column + return new ColumnNode(contextify(this)); +}; + +Column.prototype.as = function(alias) { + var context = contextify(this); + context.alias = alias; + return new ColumnNode(context); +}; + +Column.prototype.arrayAgg = function(alias) { + var context = contextify(this); + context.asArray = true; + context.alias = alias || context.name + 's'; + return new ColumnNode(context); +}; + +Column.prototype.aggregate = function(alias, aggregator) { + var context = contextify(this); + context.aggregator = aggregator.toUpperCase(); + context.alias = alias || context.name + '_' + context.aggregator.toLowerCase(); + return new ColumnNode(context); +}; + +Column.prototype.count = function(alias) { + return this.aggregate(alias, 'count'); +}; + +Column.prototype.min = function(alias) { + return this.aggregate(alias, 'min'); +}; + +Column.prototype.max = function(alias) { + return this.aggregate(alias, 'max'); +}; + +Column.prototype.sum = function(alias) { + return this.aggregate(alias, 'sum'); +}; + +Column.prototype.avg = function(alias) { + return this.aggregate(alias, 'avg'); +}; + +Column.prototype.distinct = function() { + var context = contextify(this); + context.distinct = true; + return new ColumnNode(context); +}; + +Column.prototype.toQuery = function() { + return this.toNode().toQuery(); +}; module.exports = Column; diff --git a/lib/dialect/index.js b/lib/dialect/index.js new file mode 100644 index 00000000..c26f15de --- /dev/null +++ b/lib/dialect/index.js @@ -0,0 +1,21 @@ +'use strict'; + +// given a dialect name, return the class +var getDialect = function(dialect) { + switch (dialect.toLowerCase()) { + case 'postgres': + return require('./postgres'); + case 'mysql': + 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'); + } +}; + +module.exports = getDialect; 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 new file mode 100644 index 00000000..0a63f3be --- /dev/null +++ b/lib/dialect/mysql.js @@ -0,0 +1,221 @@ +'use strict'; + +var util = require('util'); +var assert = require('assert'); +var _ = require('lodash'); + +var Mysql = function(config) { + this.output = []; + this.params = []; + this.config = config || {}; +}; + +var Postgres = require('./postgres'); + +util.inherits(Mysql, Postgres); + +Mysql.prototype._myClass = Mysql; + +Mysql.prototype._quoteCharacter = '`'; + +Mysql.prototype._arrayAggFunctionName = 'GROUP_CONCAT'; + +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; + var charset = this._queryNode.table._initialConfig.charset; + + if ( !! engine) { + result.push('ENGINE=' + engine); + } + + if ( !! charset) { + result.push('DEFAULT CHARSET=' + charset); + } + + return result; +}; + +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())[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 0fbfc50b..9b81af25 100644 --- a/lib/dialect/postgres.js +++ b/lib/dialect/postgres.js @@ -1,66 +1,285 @@ -var From = require(__dirname + '/../node/from'); -var Parameter = require(__dirname + '/../node/parameter'); -var Postgres = function() { +'use strict'; + +var _ = require('lodash'); +var assert = require('assert'); +var From = require('../node/from'); +var Select = require('../node/select'); +var Table = require('../table'); + +var Postgres = function(config) { this.output = []; this.params = []; -} + this.config = config || {}; +}; + +Postgres.prototype._myClass = Postgres; + +Postgres.prototype._arrayAggFunctionName = 'array_agg'; + +Postgres.prototype._getParameterText = function(index, value) { + if (this._disableParameterPlaceholders) { + // do not use placeholder + return this._getParameterValue(value); + } else { + // use placeholder + return this._getParameterPlaceholder(index, value); + } +}; + +Postgres.prototype._getParameterValue = function(value, quoteChar) { + // handle primitives + if (null === value) { + value = 'NULL'; + } else if ('boolean' === typeof value) { + value = value ? 'TRUE' : 'FALSE'; + } else if ('number' === typeof value) { + // number is just number + value = value; + } else if ('string' === typeof value) { + // string uses single quote by default + value = this.quote(value, quoteChar || "'"); + } else if ('object' === typeof value) { + 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 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 + 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'); + } + + // value has been converted at this point + return value; +}; + +Postgres.prototype._getParameterPlaceholder = function(index, value) { + /* jshint unused: false */ + return '$' + index; +}; Postgres.prototype.getQuery = function(queryNode) { - this.visitQuery(queryNode); - return { text: this.output.join(' '), values: this.params }; -} + // passed in a table, not a query + if (queryNode instanceof Table) { + queryNode = queryNode.select(queryNode.star()); + } + 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 }; + + // reset the internal state of this builder + this.output = []; + this.params = []; + + return query; +}; + +Postgres.prototype.getString = function(queryNode) { + // switch off parameter placeholders + var previousFlagStatus = this._disableParameterPlaceholders; + this._disableParameterPlaceholders = true; + var query; + try { + // use the same code path for query building + query = this.getQuery(queryNode); + } finally { + // always restore the flag afterwards + this._disableParameterPlaceholders = previousFlagStatus; + } + return query.text; +}; Postgres.prototype.visit = function(node) { switch(node.type) { - case 'QUERY': return this.visitQuery(node); - case 'SELECT': return this.visitSelect(node); - case 'INSERT': return this.visitInsert(node); - case 'UPDATE': return this.visitUpdate(node); - case 'FROM': return this.visitFrom(node); - case 'WHERE': return this.visitWhere(node); - case 'ORDER BY': return this.visitOrderBy(node); - case 'BINARY': return this.visitBinary(node); - case 'TABLE': return this.visitTable(node); - case 'COLUMN': return this.visitColumn(node); - case 'JOIN': return this.visitJoin(node); - case 'TEXT': return node.text; - case 'UNARY': return this.visitUnary(node); - case 'PARAMETER': return this.visitParameter(node); - default: throw new Error("Unrecognized node type " + node.type); + case 'QUERY' : return this.visitQuery(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(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 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); + case 'RENAME COLUMN' : return this.visitRenameColumn(node); + case 'INDEXES' : return this.visitIndexes(node); + case 'CREATE INDEX' : return this.visitCreateIndex(node); + case 'DROP INDEX' : return this.visitDropIndex(node); + case 'FUNCTION CALL' : return this.visitFunctionCall(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': + return this.visitModifier(node); + default: + throw new Error("Unrecognized node type " + node.type); } -} +}; -Postgres.prototype.quote = function(word) { - return '"' + word + '"'; -} +Postgres.prototype._quoteCharacter = '"'; +Postgres.prototype._aliasText = ' AS '; + +Postgres.prototype.quote = function(word, quoteCharacter) { + var q; + if (quoteCharacter) { + // use the specified quote character if given + q = quoteCharacter; + } else { + q = this._quoteCharacter; + } + // 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(', ')]; - this._selectEndIndex = this.output.length + result.length; + 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; -} +}; Postgres.prototype.visitInsert = function(insert) { var self = this; - this._visitedFrom = true; - var paramNodes = insert.nodes.map(function(node) { - return self.visit(new Parameter(node.value)); - }).join(', '); - var result = [ - 'INSERT INTO', - this.visit(this._queryNode.table.toNode()), - '(' + insert.nodes.map(this.visit.bind(this)).join(', ') + ')', - 'VALUES', '(' + paramNodes + ')' - ]; + // don't use table.column for inserts + this._visitedInsert = true; + + 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(', '); + + result.push('VALUES', paramText); + + 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 - this._visitedFrom = true; + // don't auto-generate from clause var params = []; + /* jshint boss: true */ for(var i = 0, node; node = update.nodes[i]; i++) { - params = params.concat(this.visit(node) + ' = ' + this.visit(new Parameter(node.value))); + this._visitingUpdateTargetColumn = true; + var target_col = this.visit(node); + this._visitingUpdateTargetColumn = false; + params = params.concat(target_col + ' = ' + this.visit(node.value)); } var result = [ 'UPDATE', @@ -69,94 +288,948 @@ Postgres.prototype.visitUpdate = function(update) { params.join(', ') ]; return result; -} +}; + +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) { + this._visitingCreate = true; + // 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']; + if (create.options.isTemporary) result=['CREATE TEMPORARY TABLE']; + result = result.concat(create.nodes.map(this.visit.bind(this))); + result.push(this.visit(table.toNode())); + 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; +}; + +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))); + 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) + this._aliasText + this.quote(alias.alias)]; + return result; +}; + +Postgres.prototype.visitAlter = function(alter) { + this._visitingAlter = true; + // don't auto-generate from clause + var table = this._queryNode.table; + var result = [ + 'ALTER TABLE', + this.visit(table.toNode()), + alter.nodes.map(this.visit.bind(this)).join(', ') + ]; + this._visitingAlter = false; + 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) { - this._visitedFrom = true; var result = []; - result.push('FROM'); + if (from.skipFromStatement) { + result.push(','); + } else { + result.push('FROM'); + } for(var i = 0; i < from.nodes.length; i++) { result = result.concat(this.visit(from.nodes[i])); } return result; -} +}; Postgres.prototype.visitWhere = function(where) { - var result = ['WHERE', where.nodes.map(this.visit.bind(this)).join(', ')] + 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.visitOrderByValue = function(orderByValue) { + var text = this.visit(orderByValue.value); + if (orderByValue.direction) { + text += ' ' + this.visit(orderByValue.direction); + } + return [text]; +}; + +Postgres.prototype.visitGroupBy = function(groupBy) { + var result = ['GROUP BY', groupBy.nodes.map(this.visit.bind(this)).join(', ')]; + return result; +}; + +Postgres.prototype.visitHaving = function(having) { + var result = ['HAVING', having.nodes.map(this.visit.bind(this)).join(' AND ')]; + return result; +}; + +Postgres.prototype.visitPrefixUnary = function(unary) { + var text = '(' + unary.operator + ' ' + this.visit(unary.left) + ')'; + return [text]; +}; + +Postgres.prototype.visitPostfixUnary = function(unary) { + var text = '(' + this.visit(unary.left) + ' ' + unary.operator + ')'; + return [text]; +}; Postgres.prototype.visitBinary = function(binary) { - return '(' + this.visit(binary.left) + ' ' + binary.operator + ' ' + this.visit(binary.right) + ')'; -} + var self = this; -Postgres.prototype.visitUnary = function(unary) { - return '(' + this.visit(unary.left) + ' ' + unary.operator + ')'; -} + 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) { + return self.visit(node); + }).join(', ') + ')'; + } + else { + text += this.visit(binary.right); + } + text += ')'; + return [text]; +}; + +Postgres.prototype.visitTernary = function(ternary) { + var self = this; + var text = '(' + this.visit(ternary.left) + ' ' + ternary.operator + ' '; + + var visitPart = function(value) { + var text = ''; + if (Array.isArray(value)) { + text += '(' + value.map(function (node) { + return self.visit(node); + }).join(', ') + ')'; + } + else { + text += self.visit(value); + } + return text; + }; + + text += visitPart(ternary.middle); + text += ' ' + ternary.separator + ' '; + text += visitPart(ternary.right); + + text += ')'; + 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; - for(var i = 0; i < queryNode.nodes.length; i ++) { - var res = this.visit(queryNode.nodes[i]); + // need to sort the top level query nodes on visitation priority + // so select/insert/update/delete comes before from comes before where + var missingFrom = true; + var hasFrom = false; + var createView; + var isSelect = false; + var actions = []; + var targets = []; + var filters = []; + for(var i = 0; i < queryNode.nodes.length; i++) { + 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; + break; + case "FROM": + node.skipFromStatement = hasFrom; + hasFrom = true; + missingFrom = false; + targets.push(node); + break; + case "CREATE VIEW": + createView = node; + break; + default: + filters.push(node); + break; + } + } + if(!actions.length) { + // if no actions are given, guess it's a select + actions.push(new Select().add('*')); + isSelect = true; + } + 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 + 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); } - //implicit 'from' - if(!this._visitedFrom) { - var select = this.output.slice(0, this._selectEndIndex); - var from = this.visitFrom(new From().add(queryNode.table.toNode())); - var rest = this.output.slice(this._selectEndIndex); - this.output = select.concat(from).concat(rest); + // implicit 'from' + return this.output; +}; + +Postgres.prototype.visitSubquery = function(queryNode,dontParenthesize) { + // create another query builder of the current class to build the subquery + var subQuery = new this._myClass(this.config); + + // let the subquery modify this instance's params array + subQuery.params = this.params; + + // pass on the disable parameter placeholder flag + var previousFlagStatus = subQuery._disableParameterPlaceholders; + subQuery._disableParameterPlaceholders = this._disableParameterPlaceholders; + try { + subQuery.visitQuery(queryNode); + } finally { + // restore the flag + subQuery._disableParameterPlaceholders = previousFlagStatus; } - return this; -} + + var alias = queryNode.alias; + if (dontParenthesize) { + return [subQuery.output.join(' ') + (alias ? ' ' + this.quote(alias) : '')]; + } + return ['(' + subQuery.output.join(' ') + ')' + (alias ? ' ' + this.quote(alias) : '')]; +}; Postgres.prototype.visitTable = function(tableNode) { var table = tableNode.table; - var txt = table.getName(); - if(table.quote) { - txt = '"' + txt + '"'; + var txt=""; + if(table.getSchema()) { + txt = this.quote(table.getSchema()); + txt += '.'; } - if(table.alias) { - txt += ' AS ' + table.alias; + txt += this.quote(table.getName()); + if(typeof table.alias === 'string') { + txt += this._aliasText + this.quote(table.alias); } - return txt; -} + return [txt]; +}; Postgres.prototype.visitColumn = function(columnNode) { var table = columnNode.table; - var txt = ""; - if(table.alias) { - txt = table.alias; - } else if(table.quote) { - txt = '"' + table.getName() + '"'; - } else { - txt = table.getName(); + 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 && !table.alias || !!columnNode.alias)) { + if (columnNode.asArray) { + closeParen++; + txt.push(this._arrayAggFunctionName+'('); + } + + if (!!columnNode.aggregator) { + closeParen++; + txt.push(columnNode.aggregator + '('); + } + + if (columnNode.distinct === true) { + closeParen++; + txt.push('DISTINCT('); + } } - txt += '.' - if(columnNode.quote) { - return txt += ('"' + columnNode.name + '"'); + 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.push('.'); + } } - return txt += columnNode.name; -} + if (columnNode.star) { + 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 j = 0; j < closeParen; j++) { + txt.push(')'); + } + } + 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.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.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) { + 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]; +}; Postgres.prototype.visitParameter = function(parameter) { - this.params.push(parameter.value()); - return "$"+this.params.length; -} + // save the value into the parameters array + var value = parameter.value(); + this.params.push(value); + return parameter.isExplicit ? [] : [this._getParameterText(this.params.length, value)]; +}; + +Postgres.prototype.visitDefault = function(parameter) { + /* jshint unused: false */ + return ['DEFAULT']; +}; + +Postgres.prototype.visitAddColumn = function(addColumn) { + this._visitingAddColumn = true; + var result = ['ADD COLUMN ' + this.visit(addColumn.nodes[0])]; + this._visitingAddColumn = false; + return result; +}; + +Postgres.prototype.visitDropColumn = function(dropColumn) { + return ['DROP COLUMN ' + this.visit(dropColumn.nodes[0])]; +}; + +Postgres.prototype.visitRenameColumn = function(renameColumn) { + return ['RENAME COLUMN ' + this.visit(renameColumn.nodes[0]) + ' TO ' + this.visit(renameColumn.nodes[1])]; +}; + +Postgres.prototype.visitRename = function(rename) { + return ['RENAME TO ' + this.visit(rename.nodes[0])]; +}; + +Postgres.prototype.visitIfExists = function() { + return ['IF EXISTS']; +}; + +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)); result = result.concat('ON'); result = result.concat(this.visit(join.on)); 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) { + 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.'); +}; + +Sqlite.prototype.visitDropColumn = function() { + throw new Error('SQLite does not allow dropping columns.'); +}; + +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); + this._hasAddedAColumn = true; + return result; +}; + +Sqlite.prototype.visitIndexes = function(node) { + 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 new file mode 100644 index 00000000..fe9cdaaf --- /dev/null +++ b/lib/functions.js @@ -0,0 +1,75 @@ +'use strict'; +var _ = require('lodash'); +var sliced = require('sliced'); +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) { + return function() { + // turn array-like arguments object into a true array + return new FunctionCall(name, sliced(arguments)); + }; +}; + +// creates a hash of functions for a sql instance +var getFunctions = function(functionNames) { + if (typeof functionNames === 'string') + return getFunctionCallCreator(functionNames); + + var functions = _.reduce(functionNames, function(reducer, name) { + reducer[name] = getFunctionCallCreator(name); + return reducer; + }, {}); + return functions; +}; + +// aggregate functions available to all databases +var aggregateFunctions = [ + 'AVG', + 'COUNT', + 'DISTINCT', + 'MAX', + 'MIN', + 'SUM' +]; + +// common scalar functions available to most databases +var scalarFunctions = [ + 'ABS', + 'COALESCE', + 'LEFT', + 'LENGTH', + 'LOWER', + 'LTRIM', + 'RANDOM', + 'RIGHT', + 'ROUND', + 'RTRIM', + 'SUBSTR', + 'TRIM', + 'UPPER' +]; + +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() { + return getFunctions(standardFunctionNames); +}; + +module.exports.getFunctions = getFunctions; +module.exports.getStandardFunctions = getStandardFunctions; diff --git a/lib/index.js b/lib/index.js index 71b7163d..f26ad827 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,23 +1,104 @@ -var Table = require(__dirname + '/table'); - -var sql = { - Table: Table, - define: Table.define, - select: function() { - var Query = require(__dirname + '/node/query'); - var query = new Query(); - query.select.apply(query, arguments); - return query; - }, - setDialect: function(dialect) { - switch(dialect.toLowerCase()) { - case 'postgres': - this.dialect = require(__dirname + '/dialect/postgres'); - break; - default: - throw new Error(dialect + ' is unsupported'); - } - } -}; - -module.exports = sql; +'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, config) { + this.setDialect(dialect || DEFAULT_DIALECT, config); + + // attach the standard SQL functions to this instance + this.functions = functions.getStandardFunctions(); + this.function = functions.getFunctions; +}; + +// Define a table +Sql.prototype.define = function(def) { + def = _.defaults(def || {}, { + sql: this + }); + + return Table.define(def); +}; + +// Returns a function call creator +Sql.prototype.functionCallCreator = function(name) { + return function() { + 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}); + query.select.apply(query, arguments); + 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, 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, config) { + return new Sql(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 new file mode 100644 index 00000000..5ce82cf9 --- /dev/null +++ b/lib/joiner.js @@ -0,0 +1,40 @@ +'use strict'; + +var getPrimaryKeyColumn = function(table) { + for (var i = 0; i < table.columns.length; i++) { + var col = table.columns[i]; + if (col.primaryKey) { + return col; + } + } +}; + +var findReference = function(left, right) { + // find reference + for (var i = 0; i < right.columns.length; i++) { + var col = right.columns[i]; + if (col.references) { + var leftName = left.getName(); + if (col.references === leftName || col.references.table === leftName) { + var leftCol = left[col.references.column] || getPrimaryKeyColumn(left); + return { + left: leftCol, + right: col + }; + } + } + } +}; + +module.exports = { + // auto-join two tables based on column properties + // 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 ref = findReference(left, right); + 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 new file mode 100644 index 00000000..3d7d4066 --- /dev/null +++ b/lib/node/addColumn.js @@ -0,0 +1,7 @@ +'use strict'; + +var Node = require('./index'); + +module.exports = Node.define({ + type: 'ADD COLUMN' +}); diff --git a/lib/node/alias.js b/lib/node/alias.js new file mode 100644 index 00000000..9f9855f5 --- /dev/null +++ b/lib/node/alias.js @@ -0,0 +1,29 @@ +'use strict'; + +var _ = require('lodash'); +var Node = require('./index'); + +var AliasNode = Node.define({ + type: 'ALIAS', + constructor: function(value, alias) { + Node.call(this); + + this.value = value; + this.alias = alias; + } +}); + +var AliasMixin = { + as: function(alias) { + // create an alias node + var aliasNode = new AliasNode(this, alias); + + // defaults the properties of the aliased node + _.defaults(aliasNode, this); + + return aliasNode; + } +}; + +module.exports = AliasNode; +module.exports.AliasMixin = AliasMixin; diff --git a/lib/node/alter.js b/lib/node/alter.js new file mode 100644 index 00000000..4aae4d83 --- /dev/null +++ b/lib/node/alter.js @@ -0,0 +1,7 @@ +'use strict'; + +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 b057ca22..da8eb8bd 100644 --- a/lib/node/binary.js +++ b/lib/node/binary.js @@ -1,22 +1,29 @@ -var BinaryNode = module.exports = require(__dirname).define({ +'use strict'; + +var _ = require('lodash'); +var Node = require('./index'); +var valueExpressionMixin = require('./valueExpression'); + +var valueExpressionMixed = false; +var BinaryNode = Node.define(_.extend({ type: 'BINARY', constructor: function(config) { + Node.call(this); this.left = config.left; this.operator = config.operator; 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(BinaryNode.prototype, valueExpressionMixin()); + } }, - or: function(node) { - return new BinaryNode({ - left: this, - operator: 'OR', - right: node - }); - }, - and: function(node) { - return new BinaryNode({ - left: this, - operator: 'AND', - right: node - }); - } -}) +})); + +// allow aliasing +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 17bcc824..6b145d74 100644 --- a/lib/node/column.js +++ b/lib/node/column.js @@ -1,13 +1,36 @@ -var Node = require(__dirname); +'use strict'; -var Column = Node.define({ +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.quote = config.quote; 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; + return this; } }); - -module.exports = Column; diff --git a/lib/node/create.js b/lib/node/create.js new file mode 100644 index 00000000..67cd9b3d --- /dev/null +++ b/lib/node/create.js @@ -0,0 +1,14 @@ +'use strict'; + +var Node = require('./index'); + +module.exports = Node.define({ + type: 'CREATE', + + constructor: function(isTemporary) { + Node.call(this); + + this.options = { isTemporary: isTemporary}; + }, + +}); diff --git a/lib/node/createIndex.js b/lib/node/createIndex.js new file mode 100644 index 00000000..8f8f0d04 --- /dev/null +++ b/lib/node/createIndex.js @@ -0,0 +1,63 @@ +'use strict'; + +var Node = require('./'); +var sliced = require('sliced'); + +module.exports = Node.define({ + type: 'CREATE INDEX', + + constructor: function(table, indexName) { + Node.call(this); + + this.table = table; + this.options = { indexName: indexName, columns: [] }; + }, + + unique: function() { + this.options.type = 'unique'; + return this; + }, + + spatial: function() { + this.options.type = 'spatial'; + return this; + }, + + fulltext: function() { + this.options.type = 'fulltext'; + return this; + }, + + using: function(algorithm) { + this.options.algorithm = algorithm; + return this; + }, + + on: function() { + var args = sliced(arguments); + this.options.columns = this.options.columns.concat(args); + return this; + }, + + withParser: function(parser) { + this.options.parser = parser; + return this; + }, + + indexName: function() { + var result = this.options.indexName; + + if (!result) { + var columns = this.options.columns.map(function(col) { + var column = col.name ? col.name : col.value.name; + return column; + }).sort(); + + result = [this.table._name]; + result = result.concat(columns); + result = result.join('_'); + } + + return result; + } +}); 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 new file mode 100644 index 00000000..b5adc894 --- /dev/null +++ b/lib/node/default.js @@ -0,0 +1,8 @@ +'use strict'; + +module.exports = require('./index').define({ + type: 'DEFAULT', + value: function() { + return; + } +}); diff --git a/lib/node/delete.js b/lib/node/delete.js new file mode 100644 index 00000000..0d02ebab --- /dev/null +++ b/lib/node/delete.js @@ -0,0 +1,7 @@ +'use strict'; + +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 new file mode 100644 index 00000000..b0428347 --- /dev/null +++ b/lib/node/drop.js @@ -0,0 +1,12 @@ +'use strict'; + +var Node = require('./index'); + +module.exports = Node.define({ + type: 'DROP', + + constructor: function(table) { + Node.call(this); + this.add(table); + } +}); diff --git a/lib/node/dropColumn.js b/lib/node/dropColumn.js new file mode 100644 index 00000000..01fe36c5 --- /dev/null +++ b/lib/node/dropColumn.js @@ -0,0 +1,7 @@ +'use strict'; + +var Node = require('./index'); + +module.exports = Node.define({ + type: 'DROP COLUMN' +}); diff --git a/lib/node/dropIndex.js b/lib/node/dropIndex.js new file mode 100644 index 00000000..9b5e020d --- /dev/null +++ b/lib/node/dropIndex.js @@ -0,0 +1,39 @@ +'use strict'; + +var Node = require('./'); + +module.exports = Node.define({ + type: 'DROP INDEX', + + constructor: function(table, indexName) { + if (!indexName) { + throw new Error('No index defined!'); + } else if (Array.isArray(indexName) && (typeof indexName[0] === 'string')) { + indexName = indexName[0]; + } else if (Array.isArray(indexName)) { + var columns = indexName.map(function(col) { return col.name; }).sort(); + indexName = [table._name].concat(columns).join('_'); + } + + Node.call(this); + + this.table = table; + this.options = { indexName: indexName }; + }, + + indexName: function() { + var result = this.options.indexName; + + if (!result) { + var columns = this.options.columns.map(function(col) { + return col.name; + }).sort(); + + result = [this.table._name]; + result = result.concat(columns); + result = result.join('_'); + } + + return result; + } +}); 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 407eaf3f..12b54565 100644 --- a/lib/node/from.js +++ b/lib/node/from.js @@ -1,4 +1,6 @@ -var Node = require(__dirname); +'use strict'; + +var Node = require('./index'); var From = Node.define({ type: 'FROM' diff --git a/lib/node/functionCall.js b/lib/node/functionCall.js new file mode 100644 index 00000000..608a3c1e --- /dev/null +++ b/lib/node/functionCall.js @@ -0,0 +1,24 @@ +'use strict'; + +var _ = require('lodash'); +var Node = require('./index'); +var ParameterNode = require('./parameter'); +var valueExpressionMixin = require('./valueExpression'); + +var FunctionCallNode = Node.define({ + type: 'FUNCTION CALL', + constructor: function(name, args) { + Node.call(this); + this.name = name; + this.addAll(args.map(ParameterNode.getNodeOrParameterNode)); + } +}); + +// mix in value expression +_.extend(FunctionCallNode.prototype, valueExpressionMixin()); + +// allow aliasing +var AliasNode = require('./alias'); +_.extend(FunctionCallNode.prototype, AliasNode.AliasMixin); + +module.exports = FunctionCallNode; diff --git a/lib/node/groupBy.js b/lib/node/groupBy.js new file mode 100644 index 00000000..53f63e94 --- /dev/null +++ b/lib/node/groupBy.js @@ -0,0 +1,7 @@ +'use strict'; + +var Node = require('./index'); + +module.exports = Node.define({ + type: 'GROUP BY' +}); diff --git a/lib/node/having.js b/lib/node/having.js new file mode 100644 index 00000000..6f1523ac --- /dev/null +++ b/lib/node/having.js @@ -0,0 +1,7 @@ +'use strict'; + +var Node = require('./index'); + +module.exports = Node.define({ + type: 'HAVING' +}); diff --git a/lib/node/ifExists.js b/lib/node/ifExists.js new file mode 100644 index 00000000..c26df660 --- /dev/null +++ b/lib/node/ifExists.js @@ -0,0 +1,7 @@ +'use strict'; + +var Node = require('./index'); + +module.exports = Node.define({ + type: 'IF EXISTS' +}); diff --git a/lib/node/ifNotExists.js b/lib/node/ifNotExists.js new file mode 100644 index 00000000..d731ccb2 --- /dev/null +++ b/lib/node/ifNotExists.js @@ -0,0 +1,7 @@ +'use strict'; + +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 9a727e3a..66c0e481 100644 --- a/lib/node/index.js +++ b/lib/node/index.js @@ -1,31 +1,101 @@ +'use strict'; + +var assert = require('assert'); +var getDialect = require('../dialect'); +var util = require('util'); + var Node = function(type) { + /* jshint unused: false */ this.nodes = []; -} +}; Node.prototype.toNode = function() { return this; -} +}; Node.prototype.add = function(node) { - this.nodes.push(typeof node === 'string' ? new TextNode(node) : node.toNode()); + assert(node, 'Error while trying to add a non-existant node to a query'); + 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; +}; + +// Before the change that introduced parallel dialects, every node could be turned +// into a query. The parallel dialects change made it impossible to change some nodes +// into a query because not all nodes are constructed with the sql instance. +var determineDialect = function(query, dialect) { + var sql = query.sql || (query.table && query.table.sql); + var Dialect; + + if (dialect) { + // dialect is specified + Dialect = getDialect(dialect); + } else if (sql && sql.dialect) { + // dialect is not specified, use the dialect from the sql instance + Dialect = sql.dialect; + } else { + // dialect is not specified, use the default dialect + Dialect = require('../').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 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 initializeDialect(Dialect, this).getString(this); +}; + +Node.prototype.addAll = function(nodes) { + for(var i = 0, len = nodes.length; i < len; i++) { + this.add(nodes[i]); + } return this; -} +}; Node.define = function(def) { var c = function() { - Node.apply(this, arguments) - if(def.constructor) { - def.constructor.apply(this, arguments) - } + Node.call(this); + }; + // allow custom sub-class constructor + if(def.constructor && def.constructor !== {}.constructor) { + c = def.constructor; } - for(var key in Node.prototype) { - c.prototype[key] = Node.prototype[key]; - } - for(var key in def) { - c.prototype[key] = def[key]; + util.inherits(c, Node); + 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 new file mode 100644 index 00000000..b1614a9a --- /dev/null +++ b/lib/node/indexes.js @@ -0,0 +1,15 @@ +'use strict'; + +var Node = require('./'); + +var IndexesNode = Node.define({ + type: 'INDEXES', + + constructor: function(table) { + Node.call(this); + + this.table = table; + } +}); + +module.exports = IndexesNode; diff --git a/lib/node/insert.js b/lib/node/insert.js index 355938fb..9cd12fbe 100644 --- a/lib/node/insert.js +++ b/lib/node/insert.js @@ -1,7 +1,70 @@ -var Node = require(__dirname); +'use strict'; + +var DefaultNode = require('./default'); +var Node = require('./'); +var ParameterNode = require('./parameter'); var Insert = Node.define({ - type: 'INSERT' + type: 'INSERT', + constructor: function () { + Node.call(this); + this.names = []; + this.columns = []; + this.valueSets = []; + } }); module.exports = Insert; + +Insert.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 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 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. + */ +Insert.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/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 0d1f8508..f569997e 100644 --- a/lib/node/join.js +++ b/lib/node/join.js @@ -1,12 +1,26 @@ -module.exports = require(__dirname).define({ +'use strict'; + +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; - this.to = to; + this.from = from.toNode(); + this.to = to.toNode(); }, on: function(node) { this.on = node; return this; + }, + join: function(other) { + return new JoinNode('INNER', this, other); + }, + 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 30d5d74e..b97fb891 100644 --- a/lib/node/orderBy.js +++ b/lib/node/orderBy.js @@ -1,7 +1,7 @@ -var Node = require(__dirname); +'use strict'; -var OrderBy = Node.define({ +var Node = require('./index'); + +module.exports = Node.define({ type: 'ORDER BY' }); - -module.exports = OrderBy; 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 3610176c..051ed852 100644 --- a/lib/node/parameter.js +++ b/lib/node/parameter.js @@ -1,9 +1,26 @@ -module.exports = require(__dirname).define({ +'use strict'; + +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; } }); + +// wrap a value as a parameter node if value is not already a node +module.exports.getNodeOrParameterNode = function(value) { + if (value && value.toNode) { + // use toNode + return value.toNode(); + } else { + // wrap as parameter node + return new ParameterNode(value); + } +}; diff --git a/lib/node/postfixUnary.js b/lib/node/postfixUnary.js new file mode 100644 index 00000000..8c66b89f --- /dev/null +++ b/lib/node/postfixUnary.js @@ -0,0 +1,28 @@ +'use strict'; + +var _ = require('lodash'); +var Node = require('./index'); +var valueExpressionMixin = require('./valueExpression'); + +var valueExpressionMixed = false; +var PostfixUnaryNode = Node.define({ + type: 'POSTFIX UNARY', + constructor: function(config) { + Node.call(this); + this.left = config.left; + this.operator = config.operator; + + // Delay mixin to runtime, when all nodes have been defined, and + // mixin only once. ValueExpressionMixin has circular dependencies. + if (!valueExpressionMixed) { + valueExpressionMixed = true; + _.extend(PostfixUnaryNode.prototype, valueExpressionMixin()); + } + } +}); + +// allow aliasing +var AliasNode = require('./alias'); +_.extend(PostfixUnaryNode.prototype, AliasNode.AliasMixin); + +module.exports = PostfixUnaryNode; diff --git a/lib/node/prefixUnary.js b/lib/node/prefixUnary.js new file mode 100644 index 00000000..fbde69c0 --- /dev/null +++ b/lib/node/prefixUnary.js @@ -0,0 +1,28 @@ +'use strict'; + +var _ = require('lodash'); +var Node = require('./index'); +var valueExpressionMixin = require('./valueExpression'); + +var valueExpressionMixed = false; +var PrefixUnaryNode = Node.define({ + type: 'PREFIX UNARY', + constructor: function(config) { + Node.call(this); + this.left = config.left; + this.operator = config.operator; + + // Delay mixin to runtime, when all nodes have been defined, and + // mixin only once. ValueExpressionMixin has circular dependencies. + if (!valueExpressionMixed) { + valueExpressionMixed = true; + _.extend(PrefixUnaryNode.prototype, valueExpressionMixin()); + } + } +}); + +// allow aliasing +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 f48e0f44..ebb3ecfa 100644 --- a/lib/node/query.js +++ b/lib/node/query.js @@ -1,77 +1,552 @@ -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 Insert = require(__dirname + '/insert'); -var Update = require(__dirname + '/update'); +'use strict'; + +var _ = require('lodash'); +var alias = require('./alias'); +var assert = require('assert'); +var sliced = require('sliced'); +var util = require('util'); +var valueExpressionMixin = require('./valueExpression'); + +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) { + this.table = table; + this.type = type; + this.count = count; + } +}); + +// 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; + } }, + select: function() { - var select = new Select(); - var args = Array.prototype.slice.call(arguments, 0); - for(var i = 0; i < args.length; i++) { - //arg could be a column instead of a node - select.add(args[i]); + var select; + if (this._select) { + select = this._select; + } else { + select = this._select = new Select(); + this.add(select); + } + + //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++) { + 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) { + 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 + }); + }, + + from: function() { + var tableNodes = arguments; + + if (Array.isArray(arguments[0])) { + tableNodes = arguments[0]; + } + + for (var i=0; i 1) { + // allow multiple where clause arguments + var args = sliced(arguments); + 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); + } + this.whereClause = new Where(this.table); + this.whereClause.add(node); + return this.add(this.whereClause); }, + or: function(node) { - this.where.or(node); + if (!this.whereClause) return this.where(node); + this.whereClause.or(node); return this; }, + and: function(node) { - this.where.and(node); + if (!this.whereClause) return this.where(node); + this.whereClause.and(node); return this; - }, + }, + order: function() { - var args = Array.prototype.slice.call(arguments, 0); - var orderBy = new OrderBy(); - var nodes = args.forEach(function(arg) { - orderBy.add(arg.toNode()); - }); - return this.add(orderBy); + var args = getArrayOrArgsAsArray(arguments); + var orderBy; + if (args.length === 0) { + return this; + } + if (this._orderBy) { + orderBy = this._orderBy; + } else { + orderBy = this._orderBy = new OrderBy(); + this.add(orderBy); + } + orderBy.addAll(args); + return this; }, + + group: function() { + var args = getArrayOrArgsAsArray(arguments); + var groupBy = new GroupBy().addAll(args); + return this.add(groupBy); + }, + + having: function() { + var args = getArrayOrArgsAsArray(arguments); + var having = new Having().addAll(args); + return this.add(having); + }, + insert: function(o) { var self = this; - var args = Array.prototype.slice.call(arguments, 0); - //object literal - if(arguments.length == 1 && !o["toNode"]) { - args = Object.keys(o).map(function(key) { - return self.table[key].value(o[key]); - }) - } - var insert = new Insert(); - args.forEach(function(arg) { - insert.add(arg); - }); - return this.add(insert); + + 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.insert.call(self, arg); + }); + return self; + } + + if (self.insertClause) { + self.insertClause.add(args); + return self; + } 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) { - update.add(self.table[key].value(o[key])); + 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); }, - toQuery: function() { - var Dialect = require(__dirname + '/../').dialect; - return new Dialect().getQuery(this); + + parameter: function(v) { + var param = ParameterNode.getNodeOrParameterNode(v); + param.isExplicit = true; + return this.add(param); + }, + + delete: function(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(); + 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.indexesClause) { + var createIndex = new CreateIndex(this.table, indexName); + this.add(createIndex); + return createIndex; + } else { + return this.add(new Create(this.table.isTemporary)); + } + }, + + drop: function() { + 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(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 + }); + } + 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) { + 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 + }); + } + dropClause.add(column.toNode()); + this.nodes[0].add(dropClause); + return this; + }, + + 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 + }); + } + renameClause.add(oldColumn.toNode()); + renameClause.add(newColumn.toNode()); + this.nodes[0].add(renameClause); + return this; + }, + + limit: function(count) { + return this.add(new Modifier(this, 'LIMIT', count)); + }, + + offset: function(count) { + return this.add(new Modifier(this, 'OFFSET', count)); + }, + + exists: function() { + assert(this.type === 'SUBQUERY', 'exists() can only be used on a subQuery'); + return new PrefixUnaryNode({ + left: this, + operator: "EXISTS" + }); + }, + + notExists: function() { + assert(this.type === 'SUBQUERY', 'notExists() can only be used on a subQuery'); + return new PrefixUnaryNode({ + left: this, + operator: "NOT EXISTS" + }); + }, + + ifExists: function() { + this.nodes[0].unshift(new IfExists()); + return this; + }, + + ifNotExists: function() { + 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.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 new file mode 100644 index 00000000..c87bcfb3 --- /dev/null +++ b/lib/node/rename.js @@ -0,0 +1,7 @@ +'use strict'; + +var Node = require('./index'); + +module.exports = Node.define({ + type: 'RENAME' +}); diff --git a/lib/node/renameColumn.js b/lib/node/renameColumn.js new file mode 100644 index 00000000..c3c66865 --- /dev/null +++ b/lib/node/renameColumn.js @@ -0,0 +1,7 @@ +'use strict'; + +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 new file mode 100644 index 00000000..2f27d067 --- /dev/null +++ b/lib/node/returning.js @@ -0,0 +1,7 @@ +'use strict'; + +var Node = require('./index'); + +module.exports = Node.define({ + type: 'RETURNING' +}); diff --git a/lib/node/select.js b/lib/node/select.js index 999a7436..93e22538 100644 --- a/lib/node/select.js +++ b/lib/node/select.js @@ -1,5 +1,17 @@ -var Node = require(__dirname); +'use strict'; + +var Node = require('./index'); module.exports = Node.define({ - type: 'SELECT' + type: 'SELECT', + constructor: function(arg) { + Node.call(this); + 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 a02bbde0..4fca70d1 100644 --- a/lib/node/table.js +++ b/lib/node/table.js @@ -1,6 +1,10 @@ -module.exports = require(__dirname).define({ +'use strict'; + +var Node = require('./index'); +module.exports = Node.define({ type: 'TABLE', constructor: function(table) { + Node.call(this); this.table = table; } -}) +}); diff --git a/lib/node/ternary.js b/lib/node/ternary.js new file mode 100644 index 00000000..3f688ba9 --- /dev/null +++ b/lib/node/ternary.js @@ -0,0 +1,31 @@ +'use strict'; + +var _ = require('lodash'); +var Node = require('./index'); +var valueExpressionMixin = require('./valueExpression'); + +var valueExpressionMixed = false; +var TernaryNode = Node.define(_.extend({ + type: 'TERNARY', + constructor: function(config) { + Node.call(this); + this.left = config.left; + this.middle = config.middle; + this.operator = config.operator; + this.right = config.right; + this.separator = config.separator; + + // Delay mixin to runtime, when all nodes have been defined, and + // mixin only once. ValueExpressionMixin has circular dependencies. + if (!valueExpressionMixed) { + valueExpressionMixed = true; + _.extend(TernaryNode.prototype, valueExpressionMixin()); + } + }, +})); + +// allow aliasing +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 e914e86b..bf026151 100644 --- a/lib/node/text.js +++ b/lib/node/text.js @@ -1,8 +1,11 @@ -var Node = require(__dirname); +'use strict'; + +var Node = require('./index'); module.exports = Node.define({ type: 'TEXT', constructor: function(text) { + Node.call(this); this.text = 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/unary.js b/lib/node/unary.js deleted file mode 100644 index 905a8816..00000000 --- a/lib/node/unary.js +++ /dev/null @@ -1,7 +0,0 @@ -module.exports = require(__dirname).define({ - type: 'UNARY', - constructor: function(config) { - this.left = config.left, - this.operator = config.operator - } -}); diff --git a/lib/node/update.js b/lib/node/update.js index b160255f..890fbee9 100644 --- a/lib/node/update.js +++ b/lib/node/update.js @@ -1,7 +1,7 @@ -var Node = require(__dirname); +'use strict'; -var Update = Node.define({ +var Node = require('./index'); + +module.exports = Node.define({ type: 'UPDATE' }); - -module.exports = Update; diff --git a/lib/node/valueExpression.js b/lib/node/valueExpression.js new file mode 100644 index 00000000..2fa741a9 --- /dev/null +++ b/lib/node/valueExpression.js @@ -0,0 +1,160 @@ +'use strict'; + +var OrderByValueNode = require('./orderByValue'); +var ParameterNode = require('./parameter'); +var TextNode = require('./text'); + +// Process values, wrapping them in ParameterNode if necessary. +var processParams = function(val) { + return Array.isArray(val) ? val.map(ParameterNode.getNodeOrParameterNode) : ParameterNode.getNodeOrParameterNode(val); +}; + +// Value expressions can be composed to form new value expressions. +// ValueExpressionMixin is evaluated at runtime, hence the +// "thunk" around it. +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 + }); + }; + }; + + var binaryMethod = function(operator) { + return function(val) { + return new BinaryNode({ + 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) + }); + }; + }; + + 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'), + or : binaryMethod('OR'), + and : binaryMethod('AND'), + equals : binaryMethod('='), + equal : binaryMethod('='), + notEquals : binaryMethod('<>'), + notEqual : binaryMethod('<>'), + gt : binaryMethod('>'), + gte : binaryMethod('>='), + lt : binaryMethod('<'), + lte : binaryMethod('<='), + plus : binaryMethod('+'), + minus : binaryMethod('-'), + multiply : binaryMethod('*'), + divide : binaryMethod('/'), + 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'), + 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 e404d90a..5c0465fc 100644 --- a/lib/node/where.js +++ b/lib/node/where.js @@ -1,18 +1,74 @@ -var BinaryNode = require(__dirname + '/binary'); -module.exports = require(__dirname).define({ +'use strict'; + +var Node = require('./index'); +var BinaryNode = require('./binary'); +var TextNode = require('./text'); + +var normalizeNode = function(table, node) { + var result = node; + if(typeof node === 'string') { + result = new TextNode('(' + node + ')'); + } + 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) { + 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; +}; + +module.exports = Node.define({ + constructor: function(table) { + Node.call(this); + this.table = table; + }, type: 'WHERE', + add: function(node) { + node = normalizeNode(this.table, node); + return Node.prototype.add.call(this, node); + }, or: function(other) { + var right = normalizeNode(this.table, other); + // calling 'or' without an initial 'where' + if(!this.nodes.length) { + return this.add(other); + } return this.nodes.push(new BinaryNode({ left: this.nodes.pop(), operator: 'OR', - right: other + right: right })); }, and: function(other) { + var right = normalizeNode(this.table, other); return this.nodes.push(new BinaryNode({ left: this.nodes.pop(), operator: 'AND', - right: other + right: right })); } }); diff --git a/lib/table.js b/lib/table.js index f18da4d0..1408542b 100644 --- a/lib/table.js +++ b/lib/table.js @@ -1,79 +1,294 @@ -var Query = require(__dirname + '/node/query'); -var Column = require(__dirname + '/column'); -var TableNode = require(__dirname + '/node/table'); -var TextNode = require(__dirname + '/node/text'); -var JoinNode = require(__dirname + '/node/join'); +'use strict'; + +var util = require('util'); +var lodash = require('lodash'); + +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.quote = config.quote || false; + 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'); + } + 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)) { + var cols = []; + + 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; + } + for (var i = 0; i < config.columns.length; i++) { - var col = config.columns[i]; - if(typeof col === 'string') { - col = { name: col } + 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)); } - col.table = table; - var col = new Column(col); - table.addColumn(col); - }; + } 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.addColumn = function(col) { +Table.prototype.createColumn = function(col) { + if(!(col instanceof Column)) { + if(typeof col === 'string') { + col = { name: 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; +}; + +Table.prototype.addColumn = function(col, options) { + col = this.createColumn(col); + options = lodash.extend({ + noisy: true + }, options || {}); + + if(this.hasColumn(col)) { + if (options.noisy) { + throw new Error('Table ' + this._name + ' already has column or property by the name of ' + col.name); + } else { + return this; + } + } 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); - if(this[col.name]) { - throw new Error('Table ' + this._name + ' already has column or property by the name of ' + col.name); + + function snakeToCamel(snakeName) { + return snakeName.replace(/[\-_]([a-z])/g, function(m, $1){ return $1.toUpperCase(); }); } - this[col.name] = col; + + 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) { + var columnName = col instanceof Column ? col.name : col; + return this.columns.some(function(column) { + return column.property === columnName || column.name === columnName; + }); +}; + +Table.prototype.getColumn = +Table.prototype.get = +function(colName) { + for(var i = 0; i < this.columns.length; i++) { + var col = this.columns[i]; + if (colName === col.property || colName === col.name) { + return col; + } + } + 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(options) { + options = options || {}; + if (options.prefix) { + return this.columns.map(function(column) { + return this[column.name].as(options.prefix + column.name); + }.bind(this)); + } -Table.prototype.star = function() { - return new TextNode('"'+this._name+'".*'); -} + 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}); + // ColumnNode + return col.count(alias || name + '_count'); +}; Table.prototype.select = function() { - //create the query and pass it off + // create the query and pass it off var query = new Query(this); - query.select.apply(query, arguments); + if (arguments.length === 0) { + query.select.call(query, this.star()); + } else { + query.select.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); + 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.update = function() { +Table.prototype.replace = function() { var query = new Query(this); - query.update.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; -} +}; Table.prototype.toNode = function() { return new TableNode(this); -} +}; Table.prototype.join = function(other) { - return new JoinNode('INNER', this.toNode(), other.toNode()); -} + return new JoinNode('INNER', this.toNode(), other.toNode(), other); +}; + +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); +}; Table.prototype.as = function(alias) { - //TODO could this be cleaner? + // TODO could this be cleaner? var t = Table.define(this._initialConfig); t.alias = alias; return t; -} +}; + +// called in shorthand when not calling select +Table.prototype.__defineGetter__("nodes", function() { + return this.select(this.star()).nodes; +}); + +Table.prototype.and = function() { + var query = new Query(this); + query.where.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 538dad83..dc250c01 100644 --- a/package.json +++ b/package.json @@ -2,21 +2,29 @@ "author": "brianc ", "name": "sql", "description": "sql builder", - "version": "0.0.3", + "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 test/" + "test": "node_modules/.bin/mocha", + "lint": "jshint lib test", + "posttest": "jshint lib test" }, "engines": { "node": "*" }, - "dependencies": {}, + "dependencies": { + "sliced": "0.0.x", + "lodash": "4.1.x" + }, "devDependencies": { - "test-dir" : "*" + "jshint": "*", + "mocha": "*" } } 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 40d48a64..10c50737 100644 --- a/test/binary-clause-tests.js +++ b/test/binary-clause-tests.js @@ -1,21 +1,44 @@ +'use strict'; + var assert = require('assert'); -var Column = require(__dirname + '/../lib/column'); var Table = require(__dirname + '/../lib/table'); var Foo = Table.define({ name: 'foo', columns: ['baz','bar'] -}) +}); -console.log('test operators'); -assert.equal(Foo.baz.equals(1).operator, '='); -assert.equal(Foo.baz.equal(1).operator, '='); -assert.equal(Foo.baz.notEqual(1).operator, '<>'); -assert.equal(Foo.baz.notEquals(1).operator, '<>'); -assert.equal(Foo.baz.like('asdf').operator, 'LIKE'); -assert.equal(Foo.baz.isNull().operator, 'IS NULL'); -assert.equal(Foo.baz.isNotNull().operator, 'IS NOT NULL'); -assert.equal(Foo.baz.gt(1).operator, '>'); -assert.equal(Foo.baz.gte(1).operator, '>='); -assert.equal(Foo.baz.lt(1).operator, '<'); -assert.equal(Foo.baz.lte(1).operator, '<='); +test('operators', function() { + assert.equal(Foo.baz.equals(1).operator, '='); + assert.equal(Foo.baz.equal(1).operator, '='); + assert.equal(Foo.baz.notEqual(1).operator, '<>'); + assert.equal(Foo.baz.notEquals(1).operator, '<>'); + assert.equal(Foo.baz.like('asdf').operator, 'LIKE'); + assert.equal(Foo.baz.notLike('asdf').operator, 'NOT LIKE'); + assert.equal(Foo.baz.isNull().operator, 'IS NULL'); + assert.equal(Foo.baz.isNotNull().operator, 'IS NOT NULL'); + assert.equal(Foo.baz.gt(1).operator, '>'); + assert.equal(Foo.baz.gte(1).operator, '>='); + assert.equal(Foo.baz.lt(1).operator, '<'); + assert.equal(Foo.baz.lte(1).operator, '<='); + assert.equal(Foo.baz.plus(1).operator, '+'); + assert.equal(Foo.baz.minus(1).operator, '-'); + 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 2bfd184b..ebbf936d 100644 --- a/test/clause-definition.js +++ b/test/clause-definition.js @@ -1,26 +1,31 @@ +'use strict'; + var assert = require('assert'); + var Node = require(__dirname + '/../lib/node/'); -console.log('node definition'); var Bang = Node.define({ type: 'SELECT' -}) +}); var Boom = Node.define({ constructor: function(n) { + Node.call(this); this.name = n; } }); -var select = new Bang(); -assert.equal(select.type, 'SELECT'); -assert.equal(select.nodes.length, 0); +test('clause definition', function() { + var select = new Bang(); + assert.equal(select.type, 'SELECT'); + assert.equal(select.nodes.length, 0); -var q = new Boom('hai'); -assert.equal(q.nodes.length, 0); -var q2 = new Boom('bai'); -q.nodes.push(1); -assert.equal(q.nodes.length, 1); -assert.equal(q.name, 'hai'); -assert.equal(q2.nodes.length, 0); -assert.equal(q2.name, 'bai'); + var q = new Boom('hai'); + assert.equal(q.nodes.length, 0); + var q2 = new Boom('bai'); + q.nodes.push(1); + assert.equal(q.nodes.length, 1); + assert.equal(q.name, 'hai'); + assert.equal(q2.nodes.length, 0); + assert.equal(q2.name, 'bai'); +}); diff --git a/test/column-tests.js b/test/column-tests.js new file mode 100644 index 00000000..d0b95a14 --- /dev/null +++ b/test/column-tests.js @@ -0,0 +1,131 @@ +'use strict'; + +var assert = require('assert'); +var sql = require(__dirname + '/../lib'); + +describe('column', function() { + var table = sql.define({ + name: 'user', + columns: ['id', 'created', 'alias'] + }); + + it('can be accessed by property and array', function() { + assert.equal(table.created, table.columns[1], 'should be able to access created both by array and property'); + }); + + describe('toQuery()', function() { + it('works', 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"'); + }); + + it('respects count and distinct', function() { + assert.equal(table.id.count().distinct().as("userIdCount").toQuery().text, 'COUNT(DISTINCT("user"."id")) AS "userIdCount"'); + }); + + describe('in subquery with min', function() { + var subquery = table.subQuery('subTable').select(table.id.min().as('subId')); + 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/dialect-tests.js b/test/dialect-tests.js deleted file mode 100644 index fc75a713..00000000 --- a/test/dialect-tests.js +++ /dev/null @@ -1,199 +0,0 @@ -var assert = require('assert'); -var Postgres = require(__dirname + '/../lib/dialect/postgres'); -var Table = require(__dirname + '/../lib/table'); - -var test = function(expected) { - var query = expected.query; - var pgQuery = new Postgres().getQuery(query); - var expectedPgText = expected.pg; - assert.equal(pgQuery.text, expected.pg, 'Postgres text not equal\n actual: "' + pgQuery.text + '"\n expected: "' + expected.pg + '"'); - if(expected.params) { - assert.equal(expected.params.length, pgQuery.values.length); - for(var i = 0; i < expected.params.length; i++) { - assert.equal(expected.params[i], pgQuery.values[i]); - } - } -} - -var user = Table.define({ - name: 'user', - quote: true, - columns: ['id','name'] -}) - -test({ - query : user.select(user.id).from(user), - pg : 'SELECT "user".id FROM "user"' -}); - -test({ - query : user.select(user.id, user.name).from(user), - pg : 'SELECT "user".id, "user".name FROM "user"' -}); - -test({ - query : user.select(user.star()).from(user), - pg : 'SELECT "user".* FROM "user"' -}); - -test({ - query : user.select(user.id).from(user).where(user.name.equals('foo')), - pg : 'SELECT "user".id FROM "user" WHERE ("user".name = $1)', - params: ['foo'] -}); - -test({ - query : user.select(user.id).from(user).where(user.name.equals('foo').or(user.name.equals('bar'))), - pg : 'SELECT "user".id FROM "user" WHERE (("user".name = $1) OR ("user".name = $2))', - params: ['foo', 'bar'] -}); - -test({ - query : user.select(user.id).from(user).where(user.name.equals('foo').and(user.name.equals('bar'))), - pg : 'SELECT "user".id FROM "user" WHERE (("user".name = $1) AND ("user".name = $2))', - params: ['foo', 'bar'] -}); - -test({ - query : user.select(user.id).from(user).where(user.name.equals('foo')).or(user.name.equals('bar')), - pg : 'SELECT "user".id FROM "user" WHERE (("user".name = $1) OR ("user".name = $2))' -}); - -test({ - query : user.select(user.id).from(user).where(user.name.equals('foo')).or(user.name.equals('baz')).and(user.name.equals('bar')), - pg : 'SELECT "user".id FROM "user" WHERE ((("user".name = $1) OR ("user".name = $2)) AND ("user".name = $3))' -}); - -test({ - query : user.select(user.id).from(user) - .where(user.name.equals('boom') - .and(user.id.equals(1))).or(user.name.equals('bang').and(user.id.equals(2))), - pg : 'SELECT "user".id FROM "user" WHERE ((("user".name = $1) AND ("user".id = $2)) OR (("user".name = $3) AND ("user".id = $4)))' -}); - -var post = Table.define({ - name: 'post', - columns: ['id', 'userId', 'content'] -}); - -test({ - query : user.select(user.name, post.content).from(user.join(post).on(user.id.equals(post.userId))), - pg : 'SELECT "user".name, post.content FROM "user" INNER JOIN post ON ("user".id = post."userId")' -}); - -var u = user.as('u'); -test({ - query : u.select(u.name).from(u), - pg :'SELECT u.name FROM "user" AS u' -}); - -var p = post.as('p'); -test({ - query : u.select(u.name).from(u.join(p).on(u.id.equals(p.userId).and(p.id.equals(3)))), - pg : 'SELECT u.name FROM "user" AS u INNER JOIN post AS p ON ((u.id = p."userId") AND (p.id = $1))' -}); - -test({ - query : u.select(p.content, u.name).from(u.join(p).on(u.id.equals(p.userId).and(p.content.isNotNull()))), - pg : '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))' -}); - -console.log('inserting plain SQL'); -test({ - query : user.select('name').from('user').where('name <> NULL'), - pg : 'SELECT name FROM user WHERE name <> NULL' -}); - -console.log('automatic FROM on "easy" queries'); -test({ - query : post.select(post.content), - pg : 'SELECT post.content FROM post' -}); - -test({ - query : post.select(post.content).where(post.userId.equals(1)), - pg : 'SELECT post.content FROM post WHERE (post."userId" = $1)' -}); - -console.log('order by'); - -test({ - query : post.select(post.content).order(post.content), - pg : 'SELECT post.content FROM post ORDER BY post.content', -}); - -test({ - query : post.select(post.content).order(post.content, post.userId.descending), - pg : 'SELECT post.content FROM post ORDER BY post.content, (post."userId" DESC)' -}); - -test({ - query : post.select(post.content).order(post.content.asc, post.userId.desc), - pg : 'SELECT post.content FROM post ORDER BY post.content, (post."userId" DESC)' -}); - -console.log('insert'); - -test({ - query : post.insert(post.content.value('test'), post.userId.value(1)), - pg : 'INSERT INTO post (post.content, post."userId") VALUES ($1, $2)', - params: ['test', 1] -}); - -test({ - query : post.insert(post.content.value('whoah')), - pg : 'INSERT INTO post (post.content) VALUES ($1)', - params: ['whoah'] -}); - -test({ - query : post.insert({content: 'test', userId: 2}), - pg : 'INSERT INTO post (post.content, post."userId") VALUES ($1, $2)', - params: ['test', 2] -}); - -console.log('update'); - -test({ - query : post.update({content: 'test'}), - pg : 'UPDATE post SET post.content = $1', - params: ['test'] -}); - -test({ - query : post.update({content: 'test', userId: 3}), - pg : 'UPDATE post SET post.content = $1, post."userId" = $2', - params: ['test', 3] -}); - -test({ - query : post.update({content: 'test', userId: 3}).where(post.content.equals('no')), - pg : 'UPDATE post SET post.content = $1, post."userId" = $2 WHERE (post.content = $3)', - params: ['test', 3, 'no'] -}); - -console.log('IGNORE: parent queries'); -var ignore = function() { - var parent = post.select(post.content); - assert.textEqual(parent, 'SELECT post.content FROM post'); - var child = parent.select(post.userId).where(post.userId.equals(1)); - assert.textEqual(parent, 'SELECT post.content FROM post'); - assert.textEqual(child, 'SELECT post.content, post."userId" FROM post WHERE (post."userId" = $1)'); -} - -console.log('quoting column names'); -var comment = Table.define({ - name: 'comment', - columns: [{ - name: 'text', - quote: true - }, { - name: 'userId', - quote: false - }] -}); - -test({ - query : comment.select(comment.text, comment.userId), - pg : 'SELECT comment."text", comment.userId FROM comment', -}); diff --git a/test/dialects/aggregate-tests.js b/test/dialects/aggregate-tests.js new file mode 100644 index 00000000..59d48d57 --- /dev/null +++ b/test/dialects/aggregate-tests.js @@ -0,0 +1,477 @@ +'use strict'; + +var Harness = require('./support'); +var post = Harness.definePostTable(); +var customerAlias = Harness.defineCustomerAliasTable(); + + +Harness.test({ + query: post.select(post.count()), + pg: { + text : 'SELECT COUNT("post".*) AS "post_count" FROM "post"', + string: 'SELECT COUNT("post".*) AS "post_count" FROM "post"' + }, + sqlite: { + text : 'SELECT COUNT("post".*) AS "post_count" FROM "post"', + string: 'SELECT COUNT("post".*) AS "post_count" FROM "post"' + }, + mysql: { + 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: [] +}); + +Harness.test({ + query: post.select(post.count('post_count')), + pg: { + text : 'SELECT COUNT("post".*) AS "post_count" FROM "post"', + string: 'SELECT COUNT("post".*) AS "post_count" FROM "post"' + }, + sqlite: { + text : 'SELECT COUNT("post".*) AS "post_count" FROM "post"', + string: 'SELECT COUNT("post".*) AS "post_count" FROM "post"' + }, + msyql: { + 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: [] +}); + +Harness.test({ + query: post.select(post.count().as('post_amount')), + pg: { + text : 'SELECT COUNT("post".*) AS "post_amount" FROM "post"', + string: 'SELECT COUNT("post".*) AS "post_amount" FROM "post"' + }, + sqlite: { + text : 'SELECT COUNT("post".*) AS "post_amount" FROM "post"', + string: 'SELECT COUNT("post".*) AS "post_amount" FROM "post"' + }, + mysql: { + 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: [] +}); + +Harness.test({ + query: post.select(post.content.count()), + pg: { + text : 'SELECT COUNT("post"."content") AS "content_count" FROM "post"', + string: 'SELECT COUNT("post"."content") AS "content_count" FROM "post"' + }, + sqlite: { + text : 'SELECT COUNT("post"."content") AS "content_count" FROM "post"', + string: 'SELECT COUNT("post"."content") AS "content_count" FROM "post"' + }, + mysql: { + 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: post.select(post.content.count('content_count')), + pg: { + text : 'SELECT COUNT("post"."content") AS "content_count" FROM "post"', + string: 'SELECT COUNT("post"."content") AS "content_count" FROM "post"' + }, + sqlite: { + text : 'SELECT COUNT("post"."content") AS "content_count" FROM "post"', + string: 'SELECT COUNT("post"."content") AS "content_count" FROM "post"' + }, + mysql: { + 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: post.select(post.content.count().as('content_count')), + pg: { + text : 'SELECT COUNT("post"."content") AS "content_count" FROM "post"', + string: 'SELECT COUNT("post"."content") AS "content_count" FROM "post"' + }, + sqlite: { + text : 'SELECT COUNT("post"."content") AS "content_count" FROM "post"', + string: 'SELECT COUNT("post"."content") AS "content_count" FROM "post"' + }, + mysql: { + 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: [] +}); + +Harness.test({ + query: post.select(post.id.min()), + pg: { + text : 'SELECT MIN("post"."id") AS "id_min" FROM "post"', + string: 'SELECT MIN("post"."id") AS "id_min" FROM "post"' + }, + sqlite: { + text : 'SELECT MIN("post"."id") AS "id_min" FROM "post"', + string: 'SELECT MIN("post"."id") AS "id_min" FROM "post"' + }, + mysql: { + 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: [] +}); + +Harness.test({ + query: post.select(post.id.min().as('min_id')), + pg: { + text : 'SELECT MIN("post"."id") AS "min_id" FROM "post"', + string: 'SELECT MIN("post"."id") AS "min_id" FROM "post"' + }, + sqlite: { + text : 'SELECT MIN("post"."id") AS "min_id" FROM "post"', + string: 'SELECT MIN("post"."id") AS "min_id" FROM "post"' + }, + mysql: { + 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: [] +}); + +Harness.test({ + query: post.select(post.id.min('min_id')), + pg: { + text : 'SELECT MIN("post"."id") AS "min_id" FROM "post"', + string: 'SELECT MIN("post"."id") AS "min_id" FROM "post"' + }, + sqlite: { + text : 'SELECT MIN("post"."id") AS "min_id" FROM "post"', + string: 'SELECT MIN("post"."id") AS "min_id" FROM "post"' + }, + mysql: { + 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: [] +}); + +Harness.test({ + query: post.select(post.id.max()), + pg: { + text : 'SELECT MAX("post"."id") AS "id_max" FROM "post"', + string: 'SELECT MAX("post"."id") AS "id_max" FROM "post"' + }, + sqlite: { + text : 'SELECT MAX("post"."id") AS "id_max" FROM "post"', + string: 'SELECT MAX("post"."id") AS "id_max" FROM "post"' + }, + mysql: { + 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: [] +}); + +Harness.test({ + query: post.select(post.id.max().as('max_id')), + pg: { + text : 'SELECT MAX("post"."id") AS "max_id" FROM "post"', + string: 'SELECT MAX("post"."id") AS "max_id" FROM "post"' + }, + sqlite: { + text : 'SELECT MAX("post"."id") AS "max_id" FROM "post"', + string: 'SELECT MAX("post"."id") AS "max_id" FROM "post"' + }, + mysql: { + 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: [] +}); + +Harness.test({ + query: post.select(post.id.max('max_id')), + pg: { + text : 'SELECT MAX("post"."id") AS "max_id" FROM "post"', + string: 'SELECT MAX("post"."id") AS "max_id" FROM "post"' + }, + sqlite: { + text : 'SELECT MAX("post"."id") AS "max_id" FROM "post"', + string: 'SELECT MAX("post"."id") AS "max_id" FROM "post"' + }, + mysql: { + 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: [] +}); + +Harness.test({ + query: post.select(post.id.sum()), + pg: { + text : 'SELECT SUM("post"."id") AS "id_sum" FROM "post"', + string: 'SELECT SUM("post"."id") AS "id_sum" FROM "post"' + }, + sqlite: { + text : 'SELECT SUM("post"."id") AS "id_sum" FROM "post"', + string: 'SELECT SUM("post"."id") AS "id_sum" FROM "post"' + }, + mysql: { + 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({ + query: post.select(post.id.sum().as('sum_id')), + pg: { + text : 'SELECT SUM("post"."id") AS "sum_id" FROM "post"', + string: 'SELECT SUM("post"."id") AS "sum_id" FROM "post"' + }, + sqlite: { + text : 'SELECT SUM("post"."id") AS "sum_id" FROM "post"', + string: 'SELECT SUM("post"."id") AS "sum_id" FROM "post"' + }, + mysql: { + 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({ + query: post.select(post.id.sum('sum_id')), + pg: { + text : 'SELECT SUM("post"."id") AS "sum_id" FROM "post"', + string: 'SELECT SUM("post"."id") AS "sum_id" FROM "post"' + }, + sqlite: { + text : 'SELECT SUM("post"."id") AS "sum_id" FROM "post"', + string: 'SELECT SUM("post"."id") AS "sum_id" FROM "post"' + }, + mysql: { + 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({ + query: post.select(post.id.avg()), + pg: { + text : 'SELECT AVG("post"."id") AS "id_avg" FROM "post"', + string: 'SELECT AVG("post"."id") AS "id_avg" FROM "post"' + }, + sqlite: { + text : 'SELECT AVG("post"."id") AS "id_avg" FROM "post"', + string: 'SELECT AVG("post"."id") AS "id_avg" FROM "post"' + }, + mysql: { + 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({ + query: post.select(post.id.avg().as('avg_id')), + pg: { + text : 'SELECT AVG("post"."id") AS "avg_id" FROM "post"', + string: 'SELECT AVG("post"."id") AS "avg_id" FROM "post"' + }, + sqlite: { + text : 'SELECT AVG("post"."id") AS "avg_id" FROM "post"', + string: 'SELECT AVG("post"."id") AS "avg_id" FROM "post"' + }, + mysql: { + 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({ + query: post.select(post.id.avg('avg_id')), + pg: { + text : 'SELECT AVG("post"."id") AS "avg_id" FROM "post"', + string: 'SELECT AVG("post"."id") AS "avg_id" FROM "post"' + }, + sqlite: { + text : 'SELECT AVG("post"."id") AS "avg_id" FROM "post"', + string: 'SELECT AVG("post"."id") AS "avg_id" FROM "post"' + }, + mysql: { + 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 new file mode 100644 index 00000000..32db9308 --- /dev/null +++ b/test/dialects/alias-tests.js @@ -0,0 +1,122 @@ +'use strict'; + +var Harness = require('./support'); +var customer = Harness.defineCustomerTable(); +var Sql = require('../../lib').setDialect('postgres'); + +Harness.test({ + query: customer.select(customer.name.isNull().as('nameIsNull')), + pg: { + text : 'SELECT ("customer"."name" IS NULL) AS "nameIsNull" FROM "customer"', + string: 'SELECT ("customer"."name" IS NULL) AS "nameIsNull" FROM "customer"' + }, + sqlite: { + text : 'SELECT ("customer"."name" IS NULL) AS "nameIsNull" FROM "customer"', + string: 'SELECT ("customer"."name" IS NULL) AS "nameIsNull" FROM "customer"' + }, + mysql: { + 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: [] +}); + +Harness.test({ + query: customer.select(customer.name.plus(customer.age).as('nameAndAge')).where(customer.age.gt(10).and(customer.age.lt(20))), + pg: { + 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))' + }, + sqlite: { + 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))' + }, + mysql: { + 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] +}); + +Harness.test({ + query: customer.select(customer.age.between(10, 20).as('ageBetween')), + pg: { + text : 'SELECT ("customer"."age" BETWEEN $1 AND $2) AS "ageBetween" FROM "customer"', + string: 'SELECT ("customer"."age" BETWEEN 10 AND 20) AS "ageBetween" FROM "customer"' + }, + sqlite: { + text : 'SELECT ("customer"."age" BETWEEN $1 AND $2) AS "ageBetween" FROM "customer"', + string: 'SELECT ("customer"."age" BETWEEN 10 AND 20) AS "ageBetween" FROM "customer"' + }, + mysql: { + 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 new file mode 100644 index 00000000..d050da3d --- /dev/null +++ b/test/dialects/alter-table-tests.js @@ -0,0 +1,338 @@ +'use strict'; + +var Harness = require('./support'); +var post = Harness.definePostTable(); +var Table = require(__dirname + '/../../lib/table'); + +Harness.test({ + query: post.alter().dropColumn(post.content), + pg: { + text : 'ALTER TABLE "post" DROP COLUMN "content"', + string: 'ALTER TABLE "post" DROP COLUMN "content"' + }, + sqlite: { + text : 'Sqlite cannot drop columns', + throws: true + }, + mysql: { + 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: [] +}); + +Harness.test({ + query: post.alter().dropColumn(post.content).dropColumn(post.userId), + pg: { + text : 'ALTER TABLE "post" DROP COLUMN "content", DROP COLUMN "userId"', + string: 'ALTER TABLE "post" DROP COLUMN "content", DROP COLUMN "userId"' + }, + sqlite: { + text : 'Sqlite cannot drop columns', + throws: true + }, + mysql: { + 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: [] +}); + +Harness.test({ + query: post.alter().dropColumn('content').dropColumn('userId'), + pg: { + text : 'ALTER TABLE "post" DROP COLUMN "content", DROP COLUMN "userId"', + string: 'ALTER TABLE "post" DROP COLUMN "content", DROP COLUMN "userId"' + }, + sqlite: { + text : 'Sqlite cannot drop columns', + throws: true + }, + mysql: { + 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: [] +}); + +Harness.test({ + query: post.alter().rename('posts'), + pg: { + text : 'ALTER TABLE "post" RENAME TO "posts"', + string: 'ALTER TABLE "post" RENAME TO "posts"' + }, + sqlite: { + text : 'ALTER TABLE "post" RENAME TO "posts"', + string: 'ALTER TABLE "post" RENAME TO "posts"' + }, + mysql: { + 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: [] +}); + +var group = Table.define({ + name: 'group', + columns: [{ + name: 'id', + dataType: 'varchar(100)' + }, { + name: 'userId', + dataType: 'varchar(100)' + } + ] +}); + +Harness.test({ + query: group.alter().addColumn(group.id), + pg: { + text : 'ALTER TABLE "group" ADD COLUMN "id" varchar(100)', + string: 'ALTER TABLE "group" ADD COLUMN "id" varchar(100)' + }, + sqlite: { + text : 'ALTER TABLE "group" ADD COLUMN "id" varchar(100)', + string: 'ALTER TABLE "group" ADD COLUMN "id" varchar(100)' + }, + mysql: { + 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: [] +}); + +Harness.test({ + query: group.alter().addColumn(group.id).addColumn(group.userId), + pg: { + 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)' + }, + sqlite: { + text : 'Sqlite cannot add more than one column at a time', + throws: true + }, + mysql: { + 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: [] +}); + +Harness.test({ + query: group.alter().addColumn('id', 'varchar(100)').addColumn('userId', 'varchar(100)'), + pg: { + 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)' + }, + sqlite: { + text : 'Sqlite cannot add more than one column at a time', + throws: true + }, + mysql: { + 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: [] +}); + +Harness.test({ + query: group.alter().renameColumn('userId', 'newUserId'), + pg: { + text : 'ALTER TABLE "group" RENAME COLUMN "userId" TO "newUserId"', + string: 'ALTER TABLE "group" RENAME COLUMN "userId" TO "newUserId"' + }, + mysql: { + text : 'Mysql requires data type for renaming a column', + throws: true + }, + sqlite: { + 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: [] +}); + +Harness.test({ + query: group.alter().renameColumn(group.userId, 'newUserId'), + pg: { + text : 'ALTER TABLE "group" RENAME COLUMN "userId" TO "newUserId"', + string: 'ALTER TABLE "group" RENAME COLUMN "userId" TO "newUserId"' + }, + sqlite: { + text : 'Sqlite cannot rename columns', + throws: true + }, + mysql: { + 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: [] +}); + +Harness.test({ + query: group.alter().renameColumn('userId', group.id), + pg: { + text : 'ALTER TABLE "group" RENAME COLUMN "userId" TO "id"', + string: 'ALTER TABLE "group" RENAME COLUMN "userId" TO "id"' + }, + sqlite: { + text : 'Sqlite cannot rename columns', + throws: true + }, + mysql: { + 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: [] +}); + +var UserWithSignature = Table.define({ + name: 'UserWithSignature', + columns: [{ + name: 'Signature', + dataType: "VARCHAR(255) NOT NULL DEFAULT 'Signature'" + } + ] +}); + +Harness.test({ + query: UserWithSignature.alter().renameColumn(UserWithSignature.get('Signature'), 'sig'), + pg: { + text : 'ALTER TABLE "UserWithSignature" RENAME COLUMN "Signature" TO "sig"', + string: 'ALTER TABLE "UserWithSignature" RENAME COLUMN "Signature" TO "sig"' + }, + mysql: { + text : 'ALTER TABLE `UserWithSignature` CHANGE COLUMN `Signature` `sig` VARCHAR(255) NOT NULL DEFAULT \'Signature\'', + string: 'ALTER TABLE `UserWithSignature` CHANGE COLUMN `Signature` `sig` VARCHAR(255) NOT NULL DEFAULT \'Signature\'' + }, + 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 new file mode 100644 index 00000000..69c355fe --- /dev/null +++ b/test/dialects/binary-clause-tests.js @@ -0,0 +1,103 @@ +'use strict'; + +var Harness = require('./support'); +var customer = Harness.defineCustomerTable(); +var customerAlias = Harness.defineCustomerAliasTable(); +var post = Harness.definePostTable(); + + +Harness.test({ + query: customer.select(customer.name.plus(customer.age)), + 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]' + }, + 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: [] +}); + +Harness.test({ + query: post.select(post.content.plus('!')).where(post.userId. in (customer.subQuery().select(customer.id))), + pg: { + 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"))' + }, + sqlite: { + 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"))' + }, + mysql: { + 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: ['!'] +}); + +Harness.test({ + query: post.select(post.id.plus(': ').plus(post.content)).where(post.userId.notIn(customer.subQuery().select(customer.id))), + pg: { + 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"))' + }, + sqlite: { + 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"))' + }, + mysql: { + 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 new file mode 100644 index 00000000..83d2d28e --- /dev/null +++ b/test/dialects/clause-ordering-tests.js @@ -0,0 +1,118 @@ +'use strict'; + +var Harness = require('./support'); +var user = Harness.defineUserTable(); +var post = Harness.definePostTable(); + +// FROM - SELECT +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")' + }, + sqlite: { + 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")' + }, + mysql: { + 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 +Harness.test({ + query: user.where({ + name: '' + }).from(user).select(user.id), + pg: { + text : 'SELECT "user"."id" FROM "user" WHERE ("user"."name" = $1)', + string: 'SELECT "user"."id" FROM "user" WHERE ("user"."name" = \'\')' + }, + sqlite: { + text : 'SELECT "user"."id" FROM "user" WHERE ("user"."name" = $1)', + string: 'SELECT "user"."id" FROM "user" WHERE ("user"."name" = \'\')' + }, + mysql: { + 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: [''] +}); + +// SELECT - FROM - WHERE +Harness.test({ + query: user + .select(user.name, post.content) + .from(user.join(post).on(user.id.equals(post.userId))) + .where({ + name: '' + }), + pg: { + 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" = \'\')' + }, + sqlite: { + 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" = \'\')' + }, + mysql: { + 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: [''] +}); + +// SELECT - FROM - WHERE +Harness.test({ + query: user.select(user.id).from(user).where({ + name: '' + }), + pg: { + text : 'SELECT "user"."id" FROM "user" WHERE ("user"."name" = $1)', + string: 'SELECT "user"."id" FROM "user" WHERE ("user"."name" = \'\')' + }, + sqlite: { + text : 'SELECT "user"."id" FROM "user" WHERE ("user"."name" = $1)', + string: 'SELECT "user"."id" FROM "user" WHERE ("user"."name" = \'\')' + }, + mysql: { + 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 new file mode 100644 index 00000000..b1150617 --- /dev/null +++ b/test/dialects/create-table-tests.js @@ -0,0 +1,761 @@ +'use strict'; + +var Table = require(__dirname + '/../../lib/table'); +var Harness = require('./support'); + +var group = Table.define({ + name: 'group', + columns: [{ + name: 'id', + dataType: 'varchar(100)' + }, { + name: 'user_id', + dataType: 'varchar(100)' + } + ] +}); + +Harness.test({ + query: group.create(), + pg: { + text : 'CREATE TABLE "group" ("id" varchar(100), "user_id" varchar(100))', + string: 'CREATE TABLE "group" ("id" varchar(100), "user_id" varchar(100))' + }, + sqlite: { + text : 'CREATE TABLE "group" ("id" varchar(100), "user_id" varchar(100))', + string: 'CREATE TABLE "group" ("id" varchar(100), "user_id" varchar(100))' + }, + mysql: { + 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: [] +}); + +Harness.test({ + query: group.create().ifNotExists(), + pg: { + 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))' + }, + sqlite: { + 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))' + }, + mysql: { + 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: [] +}); + +Harness.test({ + query: Table.define({ + name: 'user', + columns: [{ + name: 'id', + dataType: 'varchar(100)' + } + ], + engine: 'InnoDB' + }).create(), + pg: { + text : 'CREATE TABLE "user" ("id" varchar(100))', + string: 'CREATE TABLE "user" ("id" varchar(100))' + }, + sqlite: { + text : 'CREATE TABLE "user" ("id" varchar(100))', + string: 'CREATE TABLE "user" ("id" varchar(100))' + }, + 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))' + } +}); + +Harness.test({ + query: Table.define({ + name: 'user', + columns: [{ + name: 'id', + dataType: 'varchar(100)' + } + ], + charset: 'latin1' + }).create(), + pg: { + text : 'CREATE TABLE "user" ("id" varchar(100))', + string: 'CREATE TABLE "user" ("id" varchar(100))' + }, + sqlite: { + text : 'CREATE TABLE "user" ("id" varchar(100))', + string: 'CREATE TABLE "user" ("id" varchar(100))' + }, + 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))' + } +}); + +Harness.test({ + query: Table.define({ + name: 'user', + columns: [{ + name: 'id', + dataType: 'varchar(100)' + } + ], + engine: 'MyISAM', + charset: 'latin1' + }).create(), + pg: { + text : 'CREATE TABLE "user" ("id" varchar(100))', + string: 'CREATE TABLE "user" ("id" varchar(100))' + }, + sqlite: { + text : 'CREATE TABLE "user" ("id" varchar(100))', + string: 'CREATE TABLE "user" ("id" varchar(100))' + }, + 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 new file mode 100644 index 00000000..51fe3026 --- /dev/null +++ b/test/dialects/delete-tests.js @@ -0,0 +1,169 @@ +'use strict'; + +var Harness = require('./support'); +var post = Harness.definePostTable(); +var user = Harness.defineUserTable(); + +Harness.test({ + query: post.delete().where(post.content.equals("hello's world")), + pg: { + text : 'DELETE FROM "post" WHERE ("post"."content" = $1)', + string: 'DELETE FROM "post" WHERE ("post"."content" = \'hello\'\'s world\')' + }, + sqlite: { + text : 'DELETE FROM "post" WHERE ("post"."content" = $1)', + string: 'DELETE FROM "post" WHERE ("post"."content" = \'hello\'\'s world\')' + }, + mysql: { + 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: '' + }), + pg: { + text : 'DELETE FROM "post" WHERE ("post"."content" = $1)', + string: 'DELETE FROM "post" WHERE ("post"."content" = \'\')' + }, + sqlite: { + text : 'DELETE FROM "post" WHERE ("post"."content" = $1)', + string: 'DELETE FROM "post" WHERE ("post"."content" = \'\')' + }, + mysql: { + 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: [''] +}); + +Harness.test({ + query: post.delete({ + content: '' + }), + pg: { + text : 'DELETE FROM "post" WHERE ("post"."content" = $1)', + string: 'DELETE FROM "post" WHERE ("post"."content" = \'\')' + }, + sqlite: { + text : 'DELETE FROM "post" WHERE ("post"."content" = $1)', + string: 'DELETE FROM "post" WHERE ("post"."content" = \'\')' + }, + mysql: { + 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: [''] +}); + +Harness.test({ + query: post.delete({ + content: '' + }).or(post.content.isNull()), + pg: { + 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))' + }, + sqlite: { + 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))' + }, + mysql: { + 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 new file mode 100644 index 00000000..9f922bff --- /dev/null +++ b/test/dialects/distinct-tests.js @@ -0,0 +1,132 @@ +'use strict'; + +var Harness = require('./support'); +var user = Harness.defineUserTable(); + +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.count().distinct().as('count')), + pg: { + text : 'SELECT COUNT(DISTINCT("user"."id")) AS "count" FROM "user"', + string: 'SELECT COUNT(DISTINCT("user"."id")) AS "count" FROM "user"' + }, + sqlite: { + text : 'SELECT COUNT(DISTINCT("user"."id")) AS "count" FROM "user"', + string: 'SELECT COUNT(DISTINCT("user"."id")) AS "count" FROM "user"' + }, + mysql: { + 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 new file mode 100644 index 00000000..eb183e64 --- /dev/null +++ b/test/dialects/drop-table-tests.js @@ -0,0 +1,96 @@ +'use strict'; + +var Harness = require('./support'); +var post = Harness.definePostTable(); + +Harness.test({ + query: post.drop(), + pg: { + text : 'DROP TABLE "post"', + string: 'DROP TABLE "post"' + }, + sqlite: { + text : 'DROP TABLE "post"', + string: 'DROP TABLE "post"' + }, + mysql: { + 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: [] +}); + +Harness.test({ + query: post.drop().ifExists(), + pg: { + text : 'DROP TABLE IF EXISTS "post"', + string: 'DROP TABLE IF EXISTS "post"' + }, + sqlite: { + text : 'DROP TABLE IF EXISTS "post"', + string: 'DROP TABLE IF EXISTS "post"' + }, + mysql: { + 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 new file mode 100644 index 00000000..673087db --- /dev/null +++ b/test/dialects/from-clause-tests.js @@ -0,0 +1,93 @@ +'use strict'; + +var Harness = require('./support'); +var user = Harness.defineUserTable(); +var post = Harness.definePostTable(); + +Harness.test({ + query: user.select(user.star()).from(user).from(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`' + }, + 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"' + } +}); + +Harness.test({ + query: user.select(user.star(), post.star()).from(user).from(post), + pg: { + text : 'SELECT "user".*, "post".* FROM "user" , "post"', + string: 'SELECT "user".*, "post".* FROM "user" , "post"' + }, + sqlite: { + text : 'SELECT "user".*, "post".* FROM "user" , "post"', + string: 'SELECT "user".*, "post".* FROM "user" , "post"' + }, + 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 new file mode 100644 index 00000000..673b5639 --- /dev/null +++ b/test/dialects/group-by-tests.js @@ -0,0 +1,129 @@ +'use strict'; + +var Harness = require('./support'); +var post = Harness.definePostTable(); + +Harness.test({ + query: post.select(post.content).group(post.userId), + pg: { + text : 'SELECT "post"."content" FROM "post" GROUP BY "post"."userId"', + string: 'SELECT "post"."content" FROM "post" GROUP BY "post"."userId"' + }, + sqlite: { + text : 'SELECT "post"."content" FROM "post" GROUP BY "post"."userId"', + string: 'SELECT "post"."content" FROM "post" GROUP BY "post"."userId"' + }, + mysql: { + 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: [] +}); + +Harness.test({ + query: post.select(post.content).group(post.userId, post.id), + pg: { + text : 'SELECT "post"."content" FROM "post" GROUP BY "post"."userId", "post"."id"', + string: 'SELECT "post"."content" FROM "post" GROUP BY "post"."userId", "post"."id"' + }, + sqlite: { + text : 'SELECT "post"."content" FROM "post" GROUP BY "post"."userId", "post"."id"', + string: 'SELECT "post"."content" FROM "post" GROUP BY "post"."userId", "post"."id"' + }, + mysql: { + 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: [] +}); + +Harness.test({ + query: post.select(post.content.arrayAgg()).group(post.userId), + pg: { + text : 'SELECT array_agg("post"."content") AS "contents" FROM "post" GROUP BY "post"."userId"', + string: 'SELECT array_agg("post"."content") AS "contents" FROM "post" GROUP BY "post"."userId"' + }, + sqlite: { + 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"' + }, + mysql: { + 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: [] +}); + +Harness.test({ + query: post.select(post.content.arrayAgg('post contents')).group(post.userId), + pg: { + text : 'SELECT array_agg("post"."content") AS "post contents" FROM "post" GROUP BY "post"."userId"', + string: 'SELECT array_agg("post"."content") AS "post contents" FROM "post" GROUP BY "post"."userId"' + }, + sqlite: { + 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"' + }, + mysql: { + 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: [] +}); + +Harness.test({ + query: post.select(post.content).group([post.userId, post.id]), + pg: { + text : 'SELECT "post"."content" FROM "post" GROUP BY "post"."userId", "post"."id"', + string: 'SELECT "post"."content" FROM "post" GROUP BY "post"."userId", "post"."id"' + }, + sqlite: { + text : 'SELECT "post"."content" FROM "post" GROUP BY "post"."userId", "post"."id"', + string: 'SELECT "post"."content" FROM "post" GROUP BY "post"."userId", "post"."id"' + }, + mysql: { + 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 new file mode 100644 index 00000000..5f8f9cc7 --- /dev/null +++ b/test/dialects/having-tests.js @@ -0,0 +1,79 @@ +'use strict'; + +var Harness = require('./support'); +var post = Harness.definePostTable(); + +Harness.test({ + query : post.select(post.userId, post.content.count()).group(post.userId).having(post.userId.gt(10)), + pg : { + 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)' + }, + sqlite: { + 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)' + }, + mysql : { + 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] +}); + +Harness.test({ + query : post.select(post.userId, post.content.count()).group(post.userId).having(post.userId.gt(10), post.userId.lt(100)), + pg : { + 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)' + }, + sqlite: { + 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)' + }, + mysql : { + 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] +}); + +Harness.test({ + query : post.select(post.userId, post.content.count()).group(post.userId).having([post.userId.gt(10), post.userId.lt(100)]), + pg : { + 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)' + }, + sqlite: { + 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)' + }, + mysql : { + 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/index.js b/test/dialects/index.js new file mode 100644 index 00000000..ad9a93a7 --- /dev/null +++ b/test/dialects/index.js @@ -0,0 +1 @@ +'use strict'; diff --git a/test/dialects/indexes-tests.js b/test/dialects/indexes-tests.js new file mode 100644 index 00000000..86b50449 --- /dev/null +++ b/test/dialects/indexes-tests.js @@ -0,0 +1,235 @@ +'use strict'; + +var Harness = require('./support'); +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.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`', + string: 'SHOW INDEX FROM `post`' + }, + sqlite: { + 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: [] +}); + +Harness.test({ + query: post.indexes().create('index_name').unique().using('btree').on(post.id, post.userId).withParser('foo'), + pg: { + 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' + }, + mysql: { + 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' + }, + sqlite: { + 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: [] +}); + +Harness.test({ + query: post.indexes().create().fulltext().on(post.id), + pg: { + text : 'CREATE FULLTEXT INDEX "post_id" ON "post" ("id")', + string: 'CREATE FULLTEXT INDEX "post_id" ON "post" ("id")' + }, + mysql: { + text : 'CREATE FULLTEXT INDEX `post_id` ON `post` (`id`)', + string: 'CREATE FULLTEXT INDEX `post_id` ON `post` (`id`)' + }, + sqlite: { + text : 'CREATE FULLTEXT INDEX "post_id" ON "post" ("id")', + string: 'CREATE FULLTEXT INDEX "post_id" ON "post" ("id")' + }, + params: [] +}); + +Harness.test({ + query: post.indexes().create().spatial().on(post.id), + pg: { + text : 'CREATE SPATIAL INDEX "post_id" ON "post" ("id")', + string: 'CREATE SPATIAL INDEX "post_id" ON "post" ("id")' + }, + mysql: { + text : 'CREATE SPATIAL INDEX `post_id` ON `post` (`id`)', + string: 'CREATE SPATIAL INDEX `post_id` ON `post` (`id`)' + }, + sqlite: { + text : 'CREATE SPATIAL INDEX "post_id" ON "post" ("id")', + string: 'CREATE SPATIAL INDEX "post_id" ON "post" ("id")' + }, + params: [] +}); + +Harness.test({ + query: post.indexes().create().on(post.userId, post.id), + pg: { + text : 'CREATE INDEX "post_id_userId" ON "post" ("userId","id")', + string: 'CREATE INDEX "post_id_userId" ON "post" ("userId","id")' + }, + mysql: { + text : 'CREATE INDEX `post_id_userId` ON `post` (`userId`,`id`)', + string: 'CREATE INDEX `post_id_userId` ON `post` (`userId`,`id`)' + }, + sqlite: { + 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).on(post.id), + pg: { + text : 'CREATE INDEX "post_id_userId" ON "post" ("userId","id")', + string: 'CREATE INDEX "post_id_userId" ON "post" ("userId","id")' + }, + mysql: { + text : 'CREATE INDEX `post_id_userId` ON `post` (`userId`,`id`)', + string: 'CREATE INDEX `post_id_userId` ON `post` (`userId`,`id`)' + }, + sqlite: { + 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: { + text : 'No columns defined!', + throws: true + }, + mysql: { + text : 'No columns defined!', + throws: true + }, + 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 "public"."index_name"', + string: 'DROP INDEX "public"."index_name"' + }, + mysql: { + text : 'DROP INDEX `public`.`index_name`', + string: 'DROP INDEX `public`.`index_name`' + }, + sqlite: { + text : 'DROP INDEX "public"."index_name"', + string: 'DROP INDEX "public"."index_name"' + }, + oracle: { + text : 'DROP INDEX "index_name"', + string: 'DROP INDEX "index_name"' + }, + params: [] +}); + +Harness.test({ + query: post.indexes().drop(post.userId, post.id), + pg: { + text : 'DROP INDEX "public"."post_id_userId"', + string: 'DROP INDEX "public"."post_id_userId"' + }, + mysql: { + text : 'DROP INDEX `public`.`post_id_userId`', + string: 'DROP INDEX `public`.`post_id_userId`' + }, + sqlite: { + 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 new file mode 100644 index 00000000..60ba6a62 --- /dev/null +++ b/test/dialects/insert-tests.js @@ -0,0 +1,1094 @@ +'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.insert(post.content.value('test'), post.userId.value(1)), + pg: { + text : 'INSERT INTO "post" ("content", "userId") VALUES ($1, $2)', + string: 'INSERT INTO "post" ("content", "userId") VALUES (\'test\', 1)' + }, + sqlite: { + text : 'INSERT INTO "post" ("content", "userId") VALUES ($1, $2)', + string: 'INSERT INTO "post" ("content", "userId") VALUES (\'test\', 1)' + }, + mysql: { + 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: { + text : 'INSERT INTO "post" ("content") VALUES ($1)', + string: 'INSERT INTO "post" ("content") VALUES (\'whoah\')' + }, + sqlite: { + text : 'INSERT INTO "post" ("content") VALUES ($1)', + string: 'INSERT INTO "post" ("content") VALUES (\'whoah\')' + }, + mysql: { + 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', + userId: 2 + }), + pg: { + text : 'INSERT INTO "post" ("content", "userId") VALUES ($1, $2)', + string: 'INSERT INTO "post" ("content", "userId") VALUES (\'test\', 2)' + }, + sqlite: { + text : 'INSERT INTO "post" ("content", "userId") VALUES ($1, $2)', + string: 'INSERT INTO "post" ("content", "userId") VALUES (\'test\', 2)' + }, + mysql: { + 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([{ + content: 'whoah' + }, { + content: 'hey' + } + ]), + pg: { + text : 'INSERT INTO "post" ("content") VALUES ($1), ($2)', + string: 'INSERT INTO "post" ("content") VALUES (\'whoah\'), (\'hey\')' + }, + sqlite: { + text : 'INSERT INTO "post" ("content") VALUES ($1), ($2)', + string: 'INSERT INTO "post" ("content") VALUES (\'whoah\'), (\'hey\')' + }, + mysql: { + 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'] +}); + +Harness.test({ + query: post.insert([{ + content: 'whoah', + userId: 1 + }, { + content: 'hey', + userId: 2 + } + ]), + pg: { + text : 'INSERT INTO "post" ("content", "userId") VALUES ($1, $2), ($3, $4)', + string: 'INSERT INTO "post" ("content", "userId") VALUES (\'whoah\', 1), (\'hey\', 2)' + }, + sqlite: { + text : 'INSERT INTO "post" ("content", "userId") VALUES ($1, $2), ($3, $4)', + string: 'INSERT INTO "post" ("content", "userId") VALUES (\'whoah\', 1), (\'hey\', 2)' + }, + mysql: { + 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] +}); + +// consistent order +Harness.test({ + query: post.insert([{ + content: 'whoah', + userId: 1 + }, { + userId: 2, + content: 'hey' + } + ]), + pg: { + text : 'INSERT INTO "post" ("content", "userId") VALUES ($1, $2), ($3, $4)', + string: 'INSERT INTO "post" ("content", "userId") VALUES (\'whoah\', 1), (\'hey\', 2)' + }, + sqlite: { + text : 'INSERT INTO "post" ("content", "userId") VALUES ($1, $2), ($3, $4)', + string: 'INSERT INTO "post" ("content", "userId") VALUES (\'whoah\', 1), (\'hey\', 2)' + }, + mysql: { + 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] +}); + +Harness.test({ + query: post.insert({}), + pg: { + text : 'INSERT INTO "post" DEFAULT VALUES', + string: 'INSERT INTO "post" DEFAULT VALUES' + }, + sqlite: { + text : 'INSERT INTO "post" DEFAULT VALUES', + string: 'INSERT INTO "post" DEFAULT VALUES' + }, + mysql: { + 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: [] +}); + +Harness.test({ + query: post.insert({}).returning('*'), + pg: { + text : 'INSERT INTO "post" DEFAULT VALUES RETURNING *', + string: 'INSERT INTO "post" DEFAULT VALUES RETURNING *' + }, + sqlite: { + throws: true + }, + mysql: { + throws: true + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: [] +}); + +Harness.test({ + query: post.insert({}).returning(post.star()), + pg: { + text : 'INSERT INTO "post" DEFAULT VALUES RETURNING *', + string: 'INSERT INTO "post" DEFAULT VALUES RETURNING *' + }, + sqlite: { + throws: true + }, + mysql: { + throws: true + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: [] +}); + +Harness.test({ + query: post.insert({}).returning(post.id), + pg: { + text : 'INSERT INTO "post" DEFAULT VALUES RETURNING "id"', + string: 'INSERT INTO "post" DEFAULT VALUES RETURNING "id"' + }, + sqlite: { + throws: true + }, + mysql: { + throws: true + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: [] +}); + +Harness.test({ + query: post.insert({}).returning(post.id, post.content), + pg: { + text : 'INSERT INTO "post" DEFAULT VALUES RETURNING "id", "content"', + string: 'INSERT INTO "post" DEFAULT VALUES RETURNING "id", "content"' + }, + sqlite: { + throws: true + }, + mysql: { + throws: true + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: [] +}); + +Harness.test({ + query: post.insert({}).returning([post.id, post.content]), + pg: { + text : 'INSERT INTO "post" DEFAULT VALUES RETURNING "id", "content"', + string: 'INSERT INTO "post" DEFAULT VALUES RETURNING "id", "content"' + }, + sqlite: { + throws: true + }, + mysql: { + throws: true + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: [] +}); + +// handle missing columns +Harness.test({ + query: post.insert([{ + content: 'whoah', + userId: 1 + }, { + content: 'hey' + } + ]), + pg: { + 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'] + }, + sqlite: { + text : 'Sqlite requires the same number of columns in each insert row', + throws: true + }, + mysql: { + 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({ + query: post.insert([{ + userId: 1 + }, { + content: 'hey', + userId: 2 + } + ]), + pg: { + 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'] + }, + sqlite: { + text : 'Sqlite requires the same number of columns in each insert row', + throws: true + }, + mysql: { + 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 new file mode 100644 index 00000000..2e0c6882 --- /dev/null +++ b/test/dialects/join-tests.js @@ -0,0 +1,230 @@ +'use strict'; + +var Harness = require('./support'); +var user = Harness.defineUserTable(); +var post = Harness.definePostTable(); +var comment = Harness.defineCommentTable(); + +Harness.test({ + query: user.select(user.name, post.content).from(user.join(post).on(user.id.equals(post.userId))), + 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")' + }, + sqlite: { + 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")' + }, + mysql: { + 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: [] +}); + +Harness.test({ + query: user.join(post).on(user.id.equals(post.userId)), + pg: { + text : '"user" INNER JOIN "post" ON ("user"."id" = "post"."userId")', + string: '"user" INNER JOIN "post" ON ("user"."id" = "post"."userId")' + }, + sqlite: { + text : '"user" INNER JOIN "post" ON ("user"."id" = "post"."userId")', + string: '"user" INNER JOIN "post" ON ("user"."id" = "post"."userId")' + }, + mysql: { + 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: [] +}); + +Harness.test({ + query: user + .select(user.name, post.content, comment.text) + .from( + user + .join(post).on(user.id.equals(post.userId)) + .join(comment).on(post.id.equals(comment.postId))), + pg: { + 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")' + }, + sqlite: { + 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")' + }, + mysql: { + 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: [] +}); + +Harness.test({ + query: user.select(user.name, post.content).from(user.leftJoin(post).on(user.id.equals(post.userId))), + pg: { + 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")' + }, + sqlite: { + 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")' + }, + mysql: { + 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: [] +}); + +Harness.test({ + query: user + .select(user.name, post.content) + .from( + user + .leftJoin(post).on(user.id.equals(post.userId)) + .leftJoin(comment).on(post.id.equals(comment.postId))), + pg: { + 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")' + }, + sqlite: { + 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")' + }, + mysql: { + 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: [] +}); + +var subposts = post + .subQuery('subposts') + .select( + post.content, + post.userId.as('subpostUserId')) + .from(post); + +Harness.test({ + query: user + .select(user.name, subposts.content) + .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")' + }, + 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")' + }, + 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`)' + }, + 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 new file mode 100644 index 00000000..d50f8817 --- /dev/null +++ b/test/dialects/join-to-tests.js @@ -0,0 +1,112 @@ +'use strict'; +var sql = require(__dirname + '/../../lib'); + +var Harness = require('./support'); + +var user = sql.define({ + name: 'user', + columns: { + id: { + primaryKey: true + } + } +}); + +var photo = sql.define({ + name: 'photo', + columns: { + ownerId: { + references: 'user' + } + } +}); + +var post = sql.define({ + name: 'post', + columns: { + id: { + primaryKey: true + }, + ownerId: { + references: { + table: 'user', + column: 'id' + } + } + } +}); + +Harness.test({ + query: user.joinTo(post), + pg: { + text : '"user" INNER JOIN "post" ON ("user"."id" = "post"."ownerId")', + string: '"user" INNER JOIN "post" ON ("user"."id" = "post"."ownerId")' + }, + sqlite: { + text : '"user" INNER JOIN "post" ON ("user"."id" = "post"."ownerId")', + string: '"user" INNER JOIN "post" ON ("user"."id" = "post"."ownerId")' + }, + mysql: { + 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: [] +}); + +Harness.test({ + query: post.joinTo(user), + pg: { + text : '"post" INNER JOIN "user" ON ("user"."id" = "post"."ownerId")', + string: '"post" INNER JOIN "user" ON ("user"."id" = "post"."ownerId")' + }, + sqlite: { + text : '"post" INNER JOIN "user" ON ("user"."id" = "post"."ownerId")', + string: '"post" INNER JOIN "user" ON ("user"."id" = "post"."ownerId")' + }, + mysql: { + 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: [] +}); + +Harness.test({ + query: user.joinTo(photo), + pg: { + text : '"user" INNER JOIN "photo" ON ("user"."id" = "photo"."ownerId")', + string: '"user" INNER JOIN "photo" ON ("user"."id" = "photo"."ownerId")' + }, + sqlite: { + text : '"user" INNER JOIN "photo" ON ("user"."id" = "photo"."ownerId")', + string: '"user" INNER JOIN "photo" ON ("user"."id" = "photo"."ownerId")' + }, + mysql: { + 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 new file mode 100644 index 00000000..6561c14b --- /dev/null +++ b/test/dialects/limit-and-offset-tests.js @@ -0,0 +1,105 @@ +'use strict'; + +var Harness = require('./support'); +var user = Harness.defineUserTable(); + +// For compatibility with PostgreSQL, MySQL also supports the LIMIT row_count OFFSET offset syntax. +// http://dev.mysql.com/doc/refman/5.0/en/select.html + +Harness.test({ + query: user.select(user.star()).from(user).order(user.name.asc).limit(1), + pg: { + text : 'SELECT "user".* FROM "user" ORDER BY "user"."name" LIMIT 1', + string: 'SELECT "user".* FROM "user" ORDER BY "user"."name" LIMIT 1' + }, + sqlite: { + text : 'SELECT "user".* FROM "user" ORDER BY "user"."name" LIMIT 1', + string: 'SELECT "user".* FROM "user" ORDER BY "user"."name" LIMIT 1' + }, + mysql: { + 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: [] +}); + +Harness.test({ + query: user.select(user.star()).from(user).order(user.name.asc).limit(3).offset(6), + pg: { + 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' + }, + sqlite: { + 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' + }, + mysql: { + 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: [] +}); + +Harness.test({ + query: user.select(user.star()).from(user).order(user.name.asc).offset(10), + pg: { + text : 'SELECT "user".* FROM "user" ORDER BY "user"."name" OFFSET 10', + string: 'SELECT "user".* FROM "user" ORDER BY "user"."name" OFFSET 10' + }, + sqlite: { + text : 'SELECT "user".* FROM "user" ORDER BY "user"."name" OFFSET 10', + string: 'SELECT "user".* FROM "user" ORDER BY "user"."name" OFFSET 10' + }, + mysql: { + 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: [] +}); + +Harness.test({ + query: user.select(user.star()).where({ + name: 'John' + }).offset(user.subQuery().select('FLOOR(RANDOM() * COUNT(*))').where({ + name: 'John' + })).limit(1), + pg: { + text : 'SELECT "user".* FROM "user" WHERE ("user"."name" = $1) OFFSET (SELECT FLOOR(RANDOM() * COUNT(*)) FROM "user" WHERE ("user"."name" = $2)) LIMIT 1', + string: 'SELECT "user".* FROM "user" WHERE ("user"."name" = \'John\') OFFSET (SELECT FLOOR(RANDOM() * COUNT(*)) FROM "user" WHERE ("user"."name" = \'John\')) LIMIT 1' + }, + sqlite: { + text : 'SELECT "user".* FROM "user" WHERE ("user"."name" = $1) OFFSET (SELECT FLOOR(RANDOM() * COUNT(*)) FROM "user" WHERE ("user"."name" = $2)) LIMIT 1', + string: 'SELECT "user".* FROM "user" WHERE ("user"."name" = \'John\') OFFSET (SELECT FLOOR(RANDOM() * COUNT(*)) FROM "user" WHERE ("user"."name" = \'John\')) LIMIT 1' + }, + mysql: { + 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 new file mode 100644 index 00000000..5f6b4a56 --- /dev/null +++ b/test/dialects/namespace-tests.js @@ -0,0 +1,146 @@ +'use strict'; + +var Harness = require('./support'); +var Table = require('../../lib/table'); +var user = Harness.defineUserTable(); +var post = Harness.definePostTable(); + +var u = user.as('u'); +Harness.test({ + query: u.select(u.name).from(u), + pg: { + text : 'SELECT "u"."name" FROM "user" AS "u"', + string: 'SELECT "u"."name" FROM "user" AS "u"' + }, + sqlite: { + text : 'SELECT "u"."name" FROM "user" AS "u"', + string: 'SELECT "u"."name" FROM "user" AS "u"' + }, + mysql: { + 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: [] +}); + +Harness.test({ + query: u.select(u.star()).from(u), + pg: { + text : 'SELECT "u".* FROM "user" AS "u"', + string: 'SELECT "u".* FROM "user" AS "u"' + }, + sqlite: { + text : 'SELECT "u".* FROM "user" AS "u"', + string: 'SELECT "u".* FROM "user" AS "u"' + }, + mysql: { + 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: [] +}); + +var p = post.as('p'); +Harness.test({ + query: u.select(u.name).from(u.join(p).on(u.id.equals(p.userId).and(p.id.equals(3)))), + pg: { + 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))' + }, + sqlite: { + 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))' + }, + mysql: { + 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] +}); + +Harness.test({ + query: u.select(p.content, u.name).from(u.join(p).on(u.id.equals(p.userId).and(p.content.isNotNull()))), + pg: { + 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))' + }, + sqlite: { + 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))' + }, + mysql: { + 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: [] +}); + +// the quote property isn't implemented for columns, so all columns are quoted in generated queries +var comment = Table.define({ + name: 'comment', + columns: [{ + name: 'text', + quote: true + }, { + name: 'userId', + quote: false + } + ] +}); + +Harness.test({ + query: comment.select(comment.text, comment.userId), + pg: { + text : 'SELECT "comment"."text", "comment"."userId" FROM "comment"', + string: 'SELECT "comment"."text", "comment"."userId" FROM "comment"' + }, + sqlite: { + text : 'SELECT "comment"."text", "comment"."userId" FROM "comment"', + string: 'SELECT "comment"."text", "comment"."userId" FROM "comment"' + }, + mysql: { + 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 new file mode 100644 index 00000000..a0813d8e --- /dev/null +++ b/test/dialects/order-tests.js @@ -0,0 +1,365 @@ +'use strict'; + +var Harness = require('./support'); +var post = Harness.definePostTable(); +var sql = require('../../lib'); + +Harness.test({ + query: post.select(post.content).order(post.content), + pg: { + text : 'SELECT "post"."content" FROM "post" ORDER BY "post"."content"', + string: 'SELECT "post"."content" FROM "post" ORDER BY "post"."content"' + }, + 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, post.userId.descending), + pg: { + 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' + }, + sqlite: { + 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' + }, + mysql: { + 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).order(post.content.asc, post.userId.desc), + pg: { + 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' + }, + sqlite: { + 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' + }, + mysql: { + 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).order([post.content, post.userId.descending]), + pg: { + 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' + }, + sqlite: { + 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' + }, + mysql: { + 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).order(post.content).order(post.userId.descending), + pg: { + 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' + }, + sqlite: { + 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' + }, + mysql: { + 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 new file mode 100644 index 00000000..11e330da --- /dev/null +++ b/test/dialects/schema-tests.js @@ -0,0 +1,168 @@ +'use strict'; + +var Harness = require('./support'); +var Table = require(__dirname + '/../../lib/table'); + +var userWithSchema = Table.define({ + schema: 'staging', + name: 'user', + quote: true, + columns: ['id', 'name'] +}); + +Harness.test({ + query: userWithSchema.select(userWithSchema.id).from(userWithSchema), + pg: { + text : 'SELECT "staging"."user"."id" FROM "staging"."user"', + string: 'SELECT "staging"."user"."id" FROM "staging"."user"' + }, + sqlite: { + text : 'SELECT "staging"."user"."id" FROM "staging"."user"', + string: 'SELECT "staging"."user"."id" FROM "staging"."user"' + }, + mysql: { + 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: [] +}); + +Harness.test({ + query: userWithSchema.select(userWithSchema.id, userWithSchema.name).from(userWithSchema), + pg: { + text : 'SELECT "staging"."user"."id", "staging"."user"."name" FROM "staging"."user"', + string: 'SELECT "staging"."user"."id", "staging"."user"."name" FROM "staging"."user"' + }, + sqlite: { + text : 'SELECT "staging"."user"."id", "staging"."user"."name" FROM "staging"."user"', + string: 'SELECT "staging"."user"."id", "staging"."user"."name" FROM "staging"."user"' + }, + mysql: { + 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: [] +}); + +var uws = userWithSchema.as('uws'); +Harness.test({ + query: uws.select(uws.name).from(uws), + pg: { + text : 'SELECT "uws"."name" FROM "staging"."user" AS "uws"', + string: 'SELECT "uws"."name" FROM "staging"."user" AS "uws"' + }, + sqlite: { + text : 'SELECT "uws"."name" FROM "staging"."user" AS "uws"', + string: 'SELECT "uws"."name" FROM "staging"."user" AS "uws"' + }, + mysql: { + 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: [] +}); + +var postWithSchema = Table.define({ + schema: 'dev', + name: 'post', + columns: ['id', 'userId', 'content'] +}); + +Harness.test({ + query: userWithSchema.select(userWithSchema.name, postWithSchema.content).from(userWithSchema.join(postWithSchema).on(userWithSchema.id.equals(postWithSchema.userId))), + pg: { + 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")' + }, + sqlite: { + 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")' + }, + mysql: { + 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: [] +}); + +Harness.test({ + query: uws.select(uws.name, postWithSchema.content).from(uws.join(postWithSchema).on(uws.id.equals(postWithSchema.userId))), + pg: { + 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")' + }, + sqlite: { + 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")' + }, + mysql: { + 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 new file mode 100644 index 00000000..2892c8ae --- /dev/null +++ b/test/dialects/select-tests.js @@ -0,0 +1,461 @@ +'use strict'; + +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), + pg: { + text : 'SELECT "post"."id", "post"."content" FROM "post"', + string: 'SELECT "post"."id", "post"."content" FROM "post"' + }, + sqlite: { + text : 'SELECT "post"."id", "post"."content" FROM "post"', + string: 'SELECT "post"."id", "post"."content" FROM "post"' + }, + mysql: { + 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 new file mode 100644 index 00000000..35c9a90b --- /dev/null +++ b/test/dialects/shortcut-tests.js @@ -0,0 +1,161 @@ +'use strict'; + +var Harness = require('./support'); +var user = Harness.defineUserTable(); +var post = Harness.definePostTable(); + +// shortcut: 'select * from ' +Harness.test({ + query: user, + pg: { + text : 'SELECT "user".* FROM "user"', + string: 'SELECT "user".* FROM "user"' + }, + sqlite: { + text : 'SELECT "user".* FROM "user"', + string: 'SELECT "user".* FROM "user"' + }, + mysql: { + 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.where(user.name.equals(3)), + pg: { + text : 'SELECT * FROM "user" WHERE ("user"."name" = $1)', + string: 'SELECT * FROM "user" WHERE ("user"."name" = 3)' + }, + sqlite: { + text : 'SELECT * FROM "user" WHERE ("user"."name" = $1)', + string: 'SELECT * FROM "user" WHERE ("user"."name" = 3)' + }, + mysql: { + 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] +}); + +Harness.test({ + query: user.where(user.name.equals(3)).where(user.id.equals(1)), + pg: { + text : 'SELECT * FROM "user" WHERE (("user"."name" = $1) AND ("user"."id" = $2))', + string: 'SELECT * FROM "user" WHERE (("user"."name" = 3) AND ("user"."id" = 1))' + }, + sqlite: { + text : 'SELECT * FROM "user" WHERE (("user"."name" = $1) AND ("user"."id" = $2))', + string: 'SELECT * FROM "user" WHERE (("user"."name" = 3) AND ("user"."id" = 1))' + }, + mysql: { + 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] +}); + +// shortcut: no 'from' +Harness.test({ + query: post.select(post.content), + 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: [] +}); + +Harness.test({ + query: post.select(post.content).where(post.userId.equals(1)), + pg: { + text : 'SELECT "post"."content" FROM "post" WHERE ("post"."userId" = $1)', + string: 'SELECT "post"."content" FROM "post" WHERE ("post"."userId" = 1)' + }, + sqlite: { + text : 'SELECT "post"."content" FROM "post" WHERE ("post"."userId" = $1)', + string: 'SELECT "post"."content" FROM "post" WHERE ("post"."userId" = 1)' + }, + mysql: { + 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] +}); + +Harness.test({ + query: post.where(post.content.isNull()).or({ + content: '' + }).and({ + userId: 1 + }), + pg: { + 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))' + }, + sqlite: { + 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))' + }, + mysql: { + 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 new file mode 100644 index 00000000..05d76f1f --- /dev/null +++ b/test/dialects/subquery-tests.js @@ -0,0 +1,273 @@ +'use strict'; + +var Harness = require('./support'); +var customer = Harness.defineCustomerTable(); +var user = Harness.defineUserTable(); +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( + user.name.in( + customer.subQuery().select(customer.name).where( + user.name.like('%HELLO%'))))), + pg: { + 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%\')))))' + }, + sqlite: { + 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%\')))))' + }, + mysql: { + 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%'] +}); + +Harness.test({ + query: Sql.select('*').from(user.subQuery()), + pg: { + text : 'SELECT * FROM (SELECT * FROM "user")', + string: 'SELECT * FROM (SELECT * FROM "user")' + }, + sqlite: { + text : 'SELECT * FROM (SELECT * FROM "user")', + string: 'SELECT * FROM (SELECT * FROM "user")' + }, + mysql: { + 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"' + }, + sqlite: { + 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`' + }, + 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: [] +}); + +Harness.test({ + query: customer.name.between( + customer.subQuery().select(Sql.functions.MIN(customer.name)), + customer.subQuery().select(Sql.functions.MAX(customer.name)) + ), + pg: { + 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"))' + }, + sqlite: { + 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"))' + }, + mysql: { + 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: [] +}); + +Harness.test({ + query: user.subQuery().where(user.name.equals(customer.name)).exists(), + pg: { + text : '(EXISTS (SELECT * FROM "user" WHERE ("user"."name" = "customer"."name")))', + string: '(EXISTS (SELECT * FROM "user" WHERE ("user"."name" = "customer"."name")))' + }, + sqlite: { + text : '(EXISTS (SELECT * FROM "user" WHERE ("user"."name" = "customer"."name")))', + string: '(EXISTS (SELECT * FROM "user" WHERE ("user"."name" = "customer"."name")))' + }, + mysql: { + 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 new file mode 100644 index 00000000..4a9fa427 --- /dev/null +++ b/test/dialects/support.js @@ -0,0 +1,137 @@ +'use strict'; +var assert = require('assert'); + +var Table = require('../../lib/table'); + +// specify dialect classes +var dialects = { + pg : require('../../lib/dialect/postgres'), + sqlite : require('../../lib/dialect/sqlite'), + mysql : require('../../lib/dialect/mysql'), + mssql : require('../../lib/dialect/mssql'), + oracle : require('../../lib/dialect/oracle') +}; + +module.exports = { + test: function(expected) { + // for each dialect + Object.keys(dialects).forEach(function(dialect) { + var expectedObject = expected[dialect]; + if (undefined !== expectedObject) { + + var DialectClass = dialects[dialect]; + + var title = dialect + ': ' + (expected.title || expectedObject.text || expectedObject); + test(title, function() { + + // check if this query is expected to throw + if (expectedObject.throws) { + assert.throws(function() { + new DialectClass(expectedObject.config).getQuery(expected.query); + }); + } else { + // build query for dialect + var compiledQuery = new DialectClass(expectedObject.config).getQuery(expected.query); + + // test result is correct + var expectedText = expectedObject.text || expectedObject; + assert.equal(compiledQuery.text, expectedText); + + // if params are specified then test these are correct + var expectedParams = expectedObject.params || expected.params; + if (undefined !== expectedParams) { + assert.equal(expectedParams.length, compiledQuery.values.length, 'params length'); + for (var i = 0; i < expectedParams.length; i++) { + assert.deepEqual(expectedParams[i], compiledQuery.values[i], 'param ' + (i + 1)); + } + } + } + + if (undefined !== expectedObject.string) { + // test the toString + if (expectedObject.throws) { + assert.throws(function() { + new DialectClass(expectedObject.config).getString(expected.query); + }); + } else { + var compiledString = new DialectClass(expectedObject.config).getString(expected.query); + + // test result is correct + assert.equal(compiledString, expectedObject.string); + } + } + }); + } // if + }); // forEach + }, + + defineUserTable: function() { + return Table.define({ + name: 'user', + quote: true, + columns: ['id', 'name'] + }); + }, + + definePostTable: function() { + return Table.define({ + name: 'post', + columns: ['id', 'userId', 'content', 'tags', 'length'] + }); + }, + + defineCommentTable: function() { + return Table.define({ + name: 'comment', + columns: ['postId', 'text'] + }); + }, + + defineCustomerTable: function() { + return Table.define({ + name: 'customer', + 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'} + } + }); + }, + + // This table contains column names that correspond to popularly used variables in formulas. + defineVariableTable: function() { + return Table.define({ + 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 new file mode 100644 index 00000000..6740a7c8 --- /dev/null +++ b/test/dialects/table-tests.js @@ -0,0 +1,675 @@ +'use strict'; + +var Harness = require('./support'); +var user = Harness.defineUserTable(); + +Harness.test({ + query: user.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: [] +}); + +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`' + }, + 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: [] +}); + +Harness.test({ + query: user.select(user.star()).from(user), + pg: { + text : 'SELECT "user".* FROM "user"', + string: 'SELECT "user".* FROM "user"' + }, + sqlite: { + text : 'SELECT "user".* FROM "user"', + string: 'SELECT "user".* FROM "user"' + }, + mysql: { + 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: [] +}); + +Harness.test({ + query: user.select(user.id).from(user).where(user.name.equals('foo')), + pg: { + text : 'SELECT "user"."id" FROM "user" WHERE ("user"."name" = $1)', + string: 'SELECT "user"."id" FROM "user" WHERE ("user"."name" = \'foo\')' + }, + sqlite: { + text : 'SELECT "user"."id" FROM "user" WHERE ("user"."name" = $1)', + string: 'SELECT "user"."id" FROM "user" WHERE ("user"."name" = \'foo\')' + }, + mysql: { + 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'] +}); + +Harness.test({ + query: user.select(user.id).from(user).where(user.name.equals('foo').or(user.name.equals('bar'))), + pg: { + 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\'))' + }, + sqlite: { + 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\'))' + }, + mysql: { + 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'] +}); + +Harness.test({ + query: user.select(user.id).from(user).where(user.name.equals('foo').and(user.name.equals('bar'))), + pg: { + 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\'))' + }, + sqlite: { + 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\'))' + }, + mysql: { + 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'] +}); + +Harness.test({ + query: user.select(user.id).from(user).where(user.name.equals('foo')).or(user.name.equals('bar')), + pg: { + 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\'))' + }, + sqlite: { + 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\'))' + }, + mysql: { + 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'] +}); + +Harness.test({ + query: user.select(user.id).from(user).where(user.name.equals('foo')).or(user.name.equals('baz')).and(user.name.equals('bar')), + pg: { + 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\'))' + }, + sqlite: { + 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\'))' + }, + mysql: { + 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'] +}); + +Harness.test({ + query: user.select(user.id).from(user).where(user.name.in(['foo', 'bar'])), + pg: { + text : 'SELECT "user"."id" FROM "user" WHERE ("user"."name" IN ($1, $2))', + string: 'SELECT "user"."id" FROM "user" WHERE ("user"."name" IN (\'foo\', \'bar\'))' + }, + sqlite: { + text : 'SELECT "user"."id" FROM "user" WHERE ("user"."name" IN ($1, $2))', + string: 'SELECT "user"."id" FROM "user" WHERE ("user"."name" IN (\'foo\', \'bar\'))' + }, + mysql: { + 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'] +}); + +Harness.test({ + query: user.select(user.id).from(user).where(user.name.in(['foo', 'bar']).and(user.id.equals(1))), + pg: { + 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))' + }, + sqlite: { + 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))' + }, + mysql: { + 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] +}); + +Harness.test({ + query: user.select(user.columns), + 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`' + }, + 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: [] +}); + + +Harness.test({ + query: user + .select(user.id) + .from(user) + .where( + user.name.equals('boom') + .and(user.id.equals(1))).or( + user.name.equals('bang').and(user.id.equals(2))), + pg: { + 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)))' + }, + sqlite: { + 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)))' + }, + mysql: { + 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] +}); + +Harness.test({ + query: user.select(user.name.as('user name'), user.id.as('user id')).from(user), + pg: { + 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"' + }, + sqlite: { + 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"' + }, + mysql: { + 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: [] +}); + +Harness.test({ + query: user.select(user.name.as('user name')).from(user).where(user.name.equals('brian')), + pg: { + 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\')' + }, + sqlite: { + 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\')' + }, + mysql: { + 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'] +}); + +Harness.test({ + query: user.select(user.name).from(user).where(user.name.equals('brian')), + pg: { + text : 'SELECT "user"."name" FROM "user" WHERE ("user"."name" = $1)', + string: 'SELECT "user"."name" FROM "user" WHERE ("user"."name" = \'brian\')' + }, + sqlite: { + text : 'SELECT "user"."name" FROM "user" WHERE ("user"."name" = $1)', + string: 'SELECT "user"."name" FROM "user" WHERE ("user"."name" = \'brian\')' + }, + mysql: { + 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'] +}); + +Harness.test({ + query: user.select('name').from('user').where('name <> NULL'), + pg: { + text : 'SELECT name FROM user WHERE (name <> NULL)', + string: 'SELECT name FROM user WHERE (name <> NULL)' + }, + sqlite: { + text : 'SELECT name FROM user WHERE (name <> NULL)', + string: 'SELECT name FROM user WHERE (name <> NULL)' + }, + mysql: { + 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: [] +}); + +Harness.test({ + query: user.select('name,id').from('user').where('name <> NULL'), + pg: { + text : 'SELECT name,id FROM user WHERE (name <> NULL)', + string: 'SELECT name,id FROM user WHERE (name <> NULL)' + }, + sqlite: { + text : 'SELECT name,id FROM user WHERE (name <> NULL)', + string: 'SELECT name,id FROM user WHERE (name <> NULL)' + }, + mysql: { + 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: [] +}); + +Harness.test({ + query: user.select('name', 'id').from('user').where('name <> NULL'), + pg: { + text : 'SELECT name, id FROM user WHERE (name <> NULL)', + string: 'SELECT name, id FROM user WHERE (name <> NULL)' + }, + sqlite: { + text : 'SELECT name, id FROM user WHERE (name <> NULL)', + string: 'SELECT name, id FROM user WHERE (name <> NULL)' + }, + mysql: { + 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: [] +}); + +Harness.test({ + query: user.select('name', 'id').from('user').where('name <> NULL').and('id <> NULL'), + pg: { + text : 'SELECT name, id FROM user WHERE ((name <> NULL) AND (id <> NULL))', + string: 'SELECT name, id FROM user WHERE ((name <> NULL) AND (id <> NULL))' + }, + sqlite: { + text : 'SELECT name, id FROM user WHERE ((name <> NULL) AND (id <> NULL))', + string: 'SELECT name, id FROM user WHERE ((name <> NULL) AND (id <> NULL))' + }, + mysql: { + 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: [] +}); + +Harness.test({ + query: user.select('name').from('user').where({ + name: 'brian' + }), + pg: { + text : 'SELECT name FROM user WHERE ("user"."name" = $1)', + string: 'SELECT name FROM user WHERE ("user"."name" = \'brian\')' + }, + sqlite: { + text : 'SELECT name FROM user WHERE ("user"."name" = $1)', + string: 'SELECT name FROM user WHERE ("user"."name" = \'brian\')' + }, + mysql: { + 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'] +}); + +Harness.test({ + query: user.select('name').from('user').where({ + name: 'brian', + id: 1 + }), + pg: { + 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))' + }, + sqlite: { + 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))' + }, + mysql: { + 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] +}); + +Harness.test({ + query: user.select(user.name.as('quote"quote"tick`tick`')), + pg: { + text : 'SELECT "user"."name" AS "quote""quote""tick`tick`" FROM "user"', + string: 'SELECT "user"."name" AS "quote""quote""tick`tick`" FROM "user"' + }, + sqlite: { + text : 'SELECT "user"."name" AS "quote""quote""tick`tick`" FROM "user"', + string: 'SELECT "user"."name" AS "quote""quote""tick`tick`" FROM "user"' + }, + mysql: { + 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: [] +}); + +Harness.test({ + query: user.select(user.star()).where(user.id.in(user.subQuery().select(user.id))), + pg: { + 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"))' + }, + sqlite: { + 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"))' + }, + mysql: { + 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 new file mode 100644 index 00000000..04832fbc --- /dev/null +++ b/test/dialects/ternary-clause-tests.js @@ -0,0 +1,55 @@ +'use strict'; + +var Harness = require('./support'); +var customer = Harness.defineCustomerTable(); +var post = Harness.definePostTable(); + +Harness.test({ + query: customer.select().where(customer.age.between(18, 25)), + pg: { + text : 'SELECT "customer".* FROM "customer" WHERE ("customer"."age" BETWEEN $1 AND $2)', + string: 'SELECT "customer".* FROM "customer" WHERE ("customer"."age" BETWEEN 18 AND 25)' + }, + sqlite: { + text : 'SELECT "customer".* FROM "customer" WHERE ("customer"."age" BETWEEN $1 AND $2)', + string: 'SELECT "customer".* FROM "customer" WHERE ("customer"."age" BETWEEN 18 AND 25)' + }, + mysql: { + 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] +}); + +Harness.test({ + query: post.select().where(post.userId.between(customer.subQuery().select(customer.id.min()), customer.subQuery().select(customer.id.max()))), + pg: { + 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"))' + }, + sqlite: { + 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"))' + }, + mysql: { + 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 new file mode 100644 index 00000000..53d413e8 --- /dev/null +++ b/test/dialects/tostring-tests.js @@ -0,0 +1,202 @@ +'use strict'; + +var assert = require('assert'); +var Harness = require('./support'); +var post = Harness.definePostTable(); + +// Null +Harness.test({ + query: post.content.equals(null), + pg: { + text : '("post"."content" = $1)', + string: '("post"."content" = NULL)' + }, + sqlite: { + text : '("post"."content" = $1)', + string: '("post"."content" = NULL)' + }, + mysql: { + 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] +}); + +// Number +Harness.test({ + query: post.content.equals(3.14), + pg: { + text : '("post"."content" = $1)', + string: '("post"."content" = 3.14)' + }, + sqlite: { + text : '("post"."content" = $1)', + string: '("post"."content" = 3.14)' + }, + mysql: { + 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] +}); + +// String +Harness.test({ + query: post.content.equals('hello\''), + pg: { + text : '("post"."content" = $1)', + string: '("post"."content" = \'hello\'\'\')' + }, + sqlite: { + text : '("post"."content" = $1)', + string: '("post"."content" = \'hello\'\'\')' + }, + mysql: { + 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\''] +}); + +// Array +Harness.test({ + query: post.content.equals([1, '2', null]), + pg: { + text : '("post"."content" = ($1, $2, $3))', + string: '("post"."content" = (1, \'2\', NULL))' + }, + sqlite: { + text : '("post"."content" = ($1, $2, $3))', + string: '("post"."content" = (1, \'2\', NULL))' + }, + mysql: { + 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] +}); + +// Date +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" = \'2000-01-01T00:00:00.000Z\')' + }, + 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')] +}); + +// 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')] +}); + +// Object +var customObject = { + toString: function() { + return 'secretMessage'; + } +}; + +Harness.test({ + query: post.content.equals(customObject), + pg: { + text : '("post"."content" = $1)', + string: '("post"."content" = \'secretMessage\')' + }, + sqlite: { + text : '("post"."content" = $1)', + string: '("post"."content" = \'secretMessage\')' + }, + mysql: { + 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] +}); + + +// Undefined +assert.throws(function() { + post.content.equals(undefined).toString(); +}); 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 new file mode 100644 index 00000000..875de382 --- /dev/null +++ b/test/dialects/unary-clause-tests.js @@ -0,0 +1,55 @@ +'use strict'; + +var Harness = require('./support'); +var customer = Harness.defineCustomerTable(); +var post = Harness.definePostTable(); + +Harness.test({ + query: customer.select().where(customer.age.isNotNull()), + pg: { + text : 'SELECT "customer".* FROM "customer" WHERE ("customer"."age" IS NOT NULL)', + string: 'SELECT "customer".* FROM "customer" WHERE ("customer"."age" IS NOT NULL)' + }, + sqlite: { + text : 'SELECT "customer".* FROM "customer" WHERE ("customer"."age" IS NOT NULL)', + string: 'SELECT "customer".* FROM "customer" WHERE ("customer"."age" IS NOT NULL)' + }, + mysql: { + 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: [] +}); + +Harness.test({ + query: post.select().where(post.userId. in (customer.subQuery().select(customer.id).where(customer.age.isNull()))), + pg: { + 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)))' + }, + sqlite: { + 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)))' + }, + mysql: { + 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 new file mode 100644 index 00000000..eb3d0b3e --- /dev/null +++ b/test/dialects/update-tests.js @@ -0,0 +1,246 @@ +'use strict'; + +var Harness = require('./support'); +var post = Harness.definePostTable(); +var user = Harness.defineUserTable(); +var variable = Harness.defineVariableTable(); + +Harness.test({ + query: post.update({ + content: 'test' + }), + pg: { + text : 'UPDATE "post" SET "content" = $1', + string: 'UPDATE "post" SET "content" = \'test\'' + }, + sqlite: { + text : 'UPDATE "post" SET "content" = $1', + string: 'UPDATE "post" SET "content" = \'test\'' + }, + mysql: { + 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'] +}); + +Harness.test({ + query: post.update({ + content: 'test', + userId: 3 + }), + pg: { + text : 'UPDATE "post" SET "content" = $1, "userId" = $2', + string: 'UPDATE "post" SET "content" = \'test\', "userId" = 3' + }, + sqlite: { + text : 'UPDATE "post" SET "content" = $1, "userId" = $2', + string: 'UPDATE "post" SET "content" = \'test\', "userId" = 3' + }, + mysql: { + 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] +}); + +Harness.test({ + query: post.update({ + content: null, + userId: 3 + }), + pg: { + text : 'UPDATE "post" SET "content" = $1, "userId" = $2', + string: 'UPDATE "post" SET "content" = NULL, "userId" = 3' + }, + sqlite: { + text : 'UPDATE "post" SET "content" = $1, "userId" = $2', + string: 'UPDATE "post" SET "content" = NULL, "userId" = 3' + }, + mysql: { + 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] +}); + +Harness.test({ + query: post.update({ + content: 'test', + userId: 3 + }).where(post.content.equals('no')), + pg: { + text : 'UPDATE "post" SET "content" = $1, "userId" = $2 WHERE ("post"."content" = $3)', + string: 'UPDATE "post" SET "content" = \'test\', "userId" = 3 WHERE ("post"."content" = \'no\')' + }, + sqlite: { + text : 'UPDATE "post" SET "content" = $1, "userId" = $2 WHERE ("post"."content" = $3)', + string: 'UPDATE "post" SET "content" = \'test\', "userId" = 3 WHERE ("post"."content" = \'no\')' + }, + mysql: { + 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'] +}); + +Harness.test({ + query: post.update({ + content: user.name + }).from(user).where(post.userId.equals(user.id)), + sqlite: { + 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")' + }, + pg: { + 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")' + }, + mysql: { + 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: [] +}); + +// update() needs to prefix ambiguous source columns; prefixing target columns is not allowed +Harness.test({ + query: post.update({ + userId: user.id + }).from(user).where(post.userId.equals(user.id)), + pg: { + 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")' + }, + sqlite: { + 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")' + }, + mysql: { + 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 new file mode 100644 index 00000000..d5c7fcd4 --- /dev/null +++ b/test/dialects/value-expression-tests.js @@ -0,0 +1,159 @@ +'use strict'; + +var Harness = require('./support'); +var customer = Harness.defineCustomerTable(); +var post = Harness.definePostTable(); +var v = Harness.defineVariableTable(); + +// Test composition of binary methods +, *, -, =. +Harness.test({ + query: customer.select(customer.name, customer.income.modulo(100)).where(customer.age.plus(5).multiply(customer.age.minus(2)).equals(10)), + pg: { + 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)' + }, + sqlite: { + 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)' + }, + mysql: { + 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] +}); + +// Test composition of binary (e.g. +) and unary (e.g. like) methods. +Harness.test({ + query: customer.select(customer.name).where(customer.name.like(customer.id.plus('hello'))), + pg: { + 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\'))' + }, + sqlite: { + 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\'))' + }, + mysql: { + 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'] +}); + +// Test implementing simple formulas. +// Acceleration formula. (a * t^2 / 2) + (v * t) = d +Harness.test({ + query: v.select(v.a.multiply(v.a).divide(2).plus(v.v.multiply(v.t)).equals(v.d)), + pg: { + 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"' + }, + sqlite: { + 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"' + }, + mysql: { + 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] +}); + +// Pythagorean theorem. a^2 + b^2 = c^2. +Harness.test({ + query: v.select(v.a.multiply(v.a).plus(v.b.multiply(v.b)).equals(v.c.multiply(v.c))), + pg: { + 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"' + }, + sqlite: { + 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"' + }, + mysql: { + 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 new file mode 100644 index 00000000..3282b7d9 --- /dev/null +++ b/test/dialects/where-clause-tests.js @@ -0,0 +1,205 @@ +'use strict'; + +var Harness = require('./support'); +var user = Harness.defineUserTable(); + +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.and(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([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 new file mode 100644 index 00000000..6c53072d --- /dev/null +++ b/test/function-tests.js @@ -0,0 +1,93 @@ +'use strict'; +var assert = require('assert'); + +var sql = require(__dirname + '/../lib').setDialect('postgres'); + +var user = sql.define({ + name: 'user', + columns: [ + {name: 'id'}, + {name:'email'}, + {name: 'name'}, + {name: 'age', property: 'howOld'} + ] +}); + +suite('function', function() { + test('alias function call', function() { + var upper = sql.functions.UPPER; + var aliasedUpper = upper(user.email).as('upperAlias').toQuery(); + + 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(); + + assert.equal(functionCall.text, 'UPPER($1, $2)'); + assert.equal(functionCall.values[0], 'hello'); + assert.equal(functionCall.values[1], 'world'); + }); + + test('creating function call on columns works', function() { + var upper = sql.functionCallCreator('UPPER'); + var functionCall = upper(user.id, user.email).toQuery(); + + assert.equal(functionCall.text, 'UPPER("user"."id", "user"."email")'); + assert.equal(functionCall.values.length, 0); + }); + + test('function call inside select works', function() { + var upper = sql.functionCallCreator('UPPER'); + var query = sql.select(upper(user.id, user.email)).from(user).where(user.email.equals('brian.m.carlson@gmail.com')).toQuery(); + + assert.equal(query.text, 'SELECT UPPER("user"."id", "user"."email") FROM "user" WHERE ("user"."email" = $1)'); + assert.equal(query.values[0], 'brian.m.carlson@gmail.com'); + }); + + test('standard aggregate functions with having clause', function() { + var count = sql.functions.COUNT; + var distinct = sql.functions.DISTINCT; + var distinctEmailCount = count(distinct(user.email)); + + var query = user.select(user.id, distinctEmailCount).group(user.id).having(distinctEmailCount.gt(100)).toQuery(); + + assert.equal(query.text, 'SELECT "user"."id", COUNT(DISTINCT("user"."email")) FROM "user" GROUP BY "user"."id" HAVING (COUNT(DISTINCT("user"."email")) > $1)'); + assert.equal(query.values[0], 100); + }); + + test('custom and standard functions behave the same', function() { + var standardUpper = sql.functions.UPPER; + var customUpper = sql.functionCallCreator('UPPER'); + + var standardQuery = user.select(standardUpper(user.name)).toQuery(); + var customQuery = user.select(customUpper(user.name)).toQuery(); + + var expectedQuery = 'SELECT UPPER("user"."name") FROM "user"'; + assert.equal(standardQuery.text, expectedQuery); + assert.equal(customQuery.text, expectedQuery); + }); + + test('combine function with operations', function() { + var f = sql.functions; + var query = user.select(f.AVG(f.DISTINCT(f.COUNT(user.id).plus(f.MAX(user.id))).minus(f.MIN(user.id))).multiply(100)).toQuery(); + + 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 5f324cf2..f25839e1 100644 --- a/test/index-tests.js +++ b/test/index-tests.js @@ -1,22 +1,214 @@ +'use strict'; var assert = require('assert'); -var sql = require(__dirname + '/../lib'); + +var sql = require('../lib'); var user = sql.define({ name: 'user', columns: ['id', 'email'] }); -console.log('unknown dialog throws exception'); -assert.throws(function() { - sql.setDialect('asdf'); -}) +suite('index', function() { + test('unknown dialect throws exception', function() { + assert.throws(function() { + sql.setDialect('asdf'); + }); + }); + + 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)'); + assert.equal(query.values[0], 'brian.m.carlson@gmail.com'); + }); + + test('setting dialect to postgres works', function() { + sql.setDialect('postgres'); + 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)'); + assert.equal(query.values[0], 'brian.m.carlson@gmail.com'); + }); + + 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'); + }); + + 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)'); + assert.deepEqual(sqliteQuery.values, values); -//throws before dialog is set -assert.throws(function() { - var query = sql.select(user.id).where(user.email.equals('brian.m.carlson@gmail.com')).toQuery(); -}) + assert.equal(postgresQuery.text, 'SELECT "user"."id" FROM "user" WHERE ("user"."email" = $1)'); + assert.deepEqual(postgresQuery.values, values); -sql.setDialect('postgres'); -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)'); -assert.equal(query.values[0], 'brian.m.carlson@gmail.com') + 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() { + var query = sql.select(user.id).from(user); + assert.throws(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/index.js b/test/index.js index 14b62411..eb7a60b0 100644 --- a/test/index.js +++ b/test/index.js @@ -1 +1,21 @@ -require('test-dir'); +'use strict'; + +var fs = require('fs'); +var path = require('path'); + +var testDir = __dirname; + +var directories = [ + testDir + '/dialects' +]; + +directories.forEach(function (d) { + var files = fs.readdirSync(d); + /*jshint boss: true */ + for(var i = 0, file; file = files[i]; i++) { + var filePath = path.join(d, file); + if (path.extname(file) === '.js') { + require(filePath); + } + } +}); diff --git a/test/mocha.opts b/test/mocha.opts new file mode 100644 index 00000000..850a6c2f --- /dev/null +++ b/test/mocha.opts @@ -0,0 +1,2 @@ +--reporter dot +--ui tdd diff --git a/test/select-tests.js b/test/select-tests.js index ef287b9d..a0befc4c 100644 --- a/test/select-tests.js +++ b/test/select-tests.js @@ -1,5 +1,15 @@ +'use strict'; + var assert = require('assert'); var Select = require(__dirname + '/../lib/node/select'); -var select = new Select(); -assert.equal(select.type, 'SELECT'); +var select = new Select({sql: require('../lib/index')}); + +test('has SELECT type', function() { + assert.equal(select.type, 'SELECT'); +}); + + +test('can go toQuery', function() { + assert.equal(select.toQuery().text, 'SELECT '); +}); diff --git a/test/table-tests.js b/test/table-tests.js index e80ae54d..73d43367 100644 --- a/test/table-tests.js +++ b/test/table-tests.js @@ -1,41 +1,273 @@ +'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({ + name: 'bang' + }); + + test('has name', function() { + assert.equal(table.getName(), 'bang'); + }); + + test('has no columns', function() { + assert.equal(table.columns.length, 0); + }); + + test('can add column', function() { + var col = new Column({ + table: table, + name: 'boom' + }); + + assert.equal(col.name, 'boom'); + assert.equal(col.table.getName(), 'bang'); + + table.addColumn(col); + assert.equal(table.columns.length, 1); + assert.equal(table.boom, col); + }); + + test('creates query node', function() { + var sel = table.select(table.boom); + assert.equal(sel.type, 'QUERY'); + }); + + test('creates *-query if no args is provided to select()', function() { + var sel = table.select(); + assert.ok(sel.nodes[0].nodes[0].star); + }); + + test('can be defined', function() { + var user = Table.define({ + name: 'user', + columns: ['id', 'name'] + }); + assert.equal(user.getName(), 'user'); + assert.equal(user.columns.length, 2); + assert.equal(user.columns[0].name, 'id'); + assert.equal(user.columns[1].name, 'name'); + assert.equal(user.columns[0].name, user.id.name); + assert.equal(user.id.table, user); + assert.equal(user.name.table, user); + }); +}); + +test('table with user-defined column property names', function () { + var table = Table.define({ + name: 'blah', + columns: [{ + name: 'id', + property: 'theId' + }, { + name: 'email', + property: 'uniqueEmail' + }] + }); + var cols = table.columns; + assert.equal(cols.length, 2); + assert.equal(cols[0].name, 'id'); + assert(cols[0] === table.theId, 'Expected table.theId to be the first column'); + assert(table.id === undefined, 'Expected table.id to not exist'); + assert.equal(cols[1].name, 'email'); + assert(cols[1] === table.uniqueEmail, 'Expected table.uniqueEmail to be the second column'); + assert(table.email === undefined, 'Expected table.email to not exist'); +}); + +test('table with fancier column definitions', function() { + var table = Table.define({ + name: 'blah', + columns: [{ + name: 'id', + type: 'serial', + notNull: true, + primaryKey: true + }, { + name: 'email', + type: 'text', + notNull: true, + unique: true, + anythingYouWant: 'awesome' + }] + }); + var cols = table.columns; + assert.equal(cols.length, 2); + var id = cols[0]; + assert.equal(id.name, 'id'); + assert.equal(id.type, 'serial'); + assert.equal(id.notNull, true); + assert.equal(id.primaryKey, true); + var email = cols[1]; + assert.equal(email.name, 'email'); + assert.equal(email.type, 'text'); + assert.equal(email.notNull, true); + assert.equal(email.unique, true); + assert.equal(email.anythingYouWant, 'awesome'); +}); + +test('table with object structured column definitions', function() { + var table = Table.define({ + name: 'blah', + columns: { + id: { + type: 'serial', + notNull: true, + primaryKey: true + }, + email: { + type: 'text', + notNull: true, + unique: true, + anythingYouWant: 'awesome' + } + } + }); + var cols = table.columns; + assert.equal(cols.length, 2); + var id = cols[0]; + assert.equal(id.name, 'id'); + assert.equal(id.type, 'serial'); + assert.equal(id.notNull, true); + assert.equal(id.primaryKey, true); + var email = cols[1]; + assert.equal(email.name, 'email'); + assert.equal(email.type, 'text'); + assert.equal(email.notNull, true); + assert.equal(email.unique, true); + assert.equal(email.anythingYouWant, 'awesome'); +}); + +test('table with dynamic column definition', function() { + var table = Table.define({ name: 'foo', columns: [] }); + assert.equal(table.columns.length, 0); + + table.addColumn('foo'); + assert.equal(table.columns.length, 1); + + assert.throws(function() { + table.addColumn('foo'); + }); + + assert.doesNotThrow(function() { + table.addColumn('foo', { noisy: false }); + }); + + assert.equal(table.columns.length, 1); +}); + +test('hasColumn', function() { + var table = Table.define({ name: 'foo', columns: [] }); + + assert.equal(table.hasColumn('baz'), false); + table.addColumn('baz'); + 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'); + assert.equal(typeof table.from, 'function'); +}); + +test('getColumn returns the from column', function() { + var table = Table.define({ name: 'foo', columns: [] }); + table.addColumn('from'); + 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); + }); -var table = new Table({ - name: 'bang' -}) + test('overwrite config while copying', function() { + var table = Table.define({ + name: 'foo', + schema: 'foobar', + columns: ['bar'], + snakeToCamel: true, + columnWhiteList: true + }); -assert.equal(table.getName(), 'bang'); -assert.equal(table.columns.length, 0); + var copy = table.clone({ + schema: 'test', + snakeToCamel: false, + columnWhiteList: false + }); -var col = new Column({ - table: table, - name: 'boom' -}) + assert.equal(copy.getSchema(), 'test'); + assert.equal(copy.snakeToCamel, false); + assert.equal(copy.columnWhiteList, false); + }); +}); -assert.equal(col.name, 'boom'); -assert.equal(col.table.getName(), 'bang'); +test('dialects', function () { + var sql = new Sql.Sql('mysql'); + var foo = sql.define({ name: 'foo', columns: [ 'id' ] }), + bar = sql.define({ name: 'bar', columns: [ 'id' ] }); -table.addColumn(col); -assert.equal(table.columns.length, 1); -assert.equal(table.boom, col); + var actual = foo.join(bar).on(bar.id.equals(1)).toString(); + assert.equal(actual, '`foo` INNER JOIN `bar` ON (`bar`.`id` = 1)'); -console.log('table creates query node'); -var sel = table.select(table.boom); -assert.equal(sel.type, 'QUERY'); + 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)'); +}); -console.log('table can be defined'); -var user = Table.define({ - name: 'user', - columns: ['id', 'name'] -}) +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); +}); -assert.equal(user.getName(), 'user'); -assert.equal(user.columns.length, 2); -assert.equal(user.columns[0].name, 'id'); -assert.equal(user.columns[1].name, 'name'); -assert.equal(user.columns[0].name, user.id.name) -assert.equal(user.id.table, user); -assert.equal(user.name.table, user); +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 new file mode 100644 index 00000000..1e7c975f --- /dev/null +++ b/test/ternary-clause-tests.js @@ -0,0 +1,14 @@ +'use strict'; + +var assert = require('assert'); +var Table = require(__dirname + '/../lib/table'); + +var Foo = Table.define({ + name: 'foo', + columns: ['baz','bar'] +}); + +test('operators', function() { + assert.equal(Foo.bar.between(1, 2).operator, 'BETWEEN'); + assert.equal(Foo.baz.between(1, 2).separator, 'AND'); +}); diff --git a/test/unary-clause-tests.js b/test/unary-clause-tests.js new file mode 100644 index 00000000..f5b36a1b --- /dev/null +++ b/test/unary-clause-tests.js @@ -0,0 +1,14 @@ +'use strict'; + +var assert = require('assert'); +var Table = require(__dirname + '/../lib/table'); + +var Foo = Table.define({ + name: 'foo', + columns: ['baz','bar'] +}); + +test('operators', function() { + assert.equal(Foo.bar.isNull().operator, 'IS NULL'); + assert.equal(Foo.baz.isNotNull().operator, 'IS NOT NULL'); +}); diff --git a/test/value-expression-tests.js b/test/value-expression-tests.js new file mode 100644 index 00000000..ec80415a --- /dev/null +++ b/test/value-expression-tests.js @@ -0,0 +1,18 @@ +'use strict'; + +var assert = require('assert'); +var valueExpressionMixin = require(__dirname + './../lib/node/valueExpression'); +var Node = require(__dirname + './../lib/node'); + +suite('value-expression', function() { + test("value expression mixin should not overwrite Node prototype properties", function() { + var mixin = valueExpressionMixin(); + + // make sure that the node class doesn't have any conflicting properties + for (var key in mixin) { + if (mixin.hasOwnProperty(key)) { + assert.equal(Node.prototype[key], undefined); + } + } + }); +});