diff --git a/.travis.yml b/.travis.yml index 05d299e6..07becfb2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +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/Makefile b/Makefile index 6cde1304..4d56d98a 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,17 @@ -.PHONY: jshint test - -jshint: - ./node_modules/.bin/jshint lib test +.PHONY: jshint test publish-patch test test: npm test + +patch: test + npm version patch -m "Bump version" + git push origin master --tags + npm publish + +minor: test + npm version minor -m "Bump version" + git push origin master --tags + npm publish + +jshint: + ./node_modules/.bin/jshint lib diff --git a/README.md b/README.md index 3a5493b8..e1a3af8c 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # node-sql -_sql string builder for node_ - supports PostgreSQL, mysql, Microsoft SQL Server, and sqlite dialects. +_sql string builder for node_ - supports PostgreSQL, mysql, Microsoft SQL Server, Oracle and sqlite dialects. Building SQL statements by hand is no fun, especially in a language which has clumsy support for multi-line strings. @@ -9,12 +9,22 @@ Maybe it's still not fun, but at least it's _less not fun_. [![Build Status](https://secure.travis-ci.org/brianc/node-sql.png)](http://travis-ci.org/brianc/node-sql) -## examples +## install + +```sh +$ npm install sql +``` + +## use ```js //require the module var sql = require('sql'); +//(optionally) set the SQL dialect +sql.setDialect('postgres'); +//possible dialects: mssql, mysql, postgres (default), sqlite + //first we define our tables var user = sql.define({ name: 'user', @@ -98,7 +108,7 @@ console.log(user.select().where(user.state.equals('WA')).toQuery().text); // "SELECT "user".* FROM "user" WHERE ("user"."state_or_province" = $1)" ``` -There are a __lot__ more examples included in the `test/dialects` folder. +There are a __lot__ more examples included in the [test/dialects](https://github.com/brianc/node-sql/tree/master/test/dialects) folder. We encourage you to read through them if you have any questions on usage! ## from the command line You can use the [sql-generate module](https://github.com/tmont/node-sql-generate) @@ -173,5 +183,5 @@ Usually after a few high-quality pull requests and friendly interactions we will After all, open source belongs to everyone. -##license +## license MIT diff --git a/lib/column.js b/lib/column.js index 3c5e70f5..55a3da18 100644 --- a/lib/column.js +++ b/lib/column.js @@ -20,6 +20,7 @@ var Column = function(config) { direction : new TextNode('DESC') }); this.dataType = config.dataType; + this.defaultValue = config.defaultValue; }; // mix in value expression diff --git a/lib/dialect/index.js b/lib/dialect/index.js index ac4dcb91..c26f15de 100644 --- a/lib/dialect/index.js +++ b/lib/dialect/index.js @@ -11,6 +11,8 @@ var getDialect = function(dialect) { return require('./sqlite'); case 'mssql': return require('./mssql'); + case 'oracle': + return require('./oracle'); default: throw new Error(dialect + ' is unsupported'); } diff --git a/lib/dialect/mssql.js b/lib/dialect/mssql.js index 2b7bc0a7..6e71f2e1 100644 --- a/lib/dialect/mssql.js +++ b/lib/dialect/mssql.js @@ -6,12 +6,21 @@ var util = require('util'); var assert = require('assert'); -var Mssql = function() { +/** + * 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(__dirname + '/postgres'); +var Postgres = require('./postgres'); util.inherits(Mssql, Postgres); @@ -21,7 +30,12 @@ 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; }; @@ -34,6 +48,10 @@ Mssql.prototype.visitBinary = function(binary) { 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); } @@ -94,24 +112,22 @@ Mssql.prototype.visitAlter = function(alter) { var table = self._queryNode.table; var result = ['EXEC sp_rename '+self.visit(table.toNode())+', '+self.visit(alter.nodes[0].nodes[0])]; self._visitingAlter = false; - return result + return result; } // Implement our own rename column: // PostgreSQL: ALTER TABLE "group" RENAME COLUMN "userId" TO "newUserId" - // Mssql: EXEC sp_rename [group], [userId], [newUserId] + // Mssql: EXEC sp_rename '[group].[userId]', [newUserId] function _renameColumn(){ - // TODO: implement this. Need to be able to get the [tableName.Column], which is hard to do with the current way visitXxx works - throw new Error('Mssql renaming columns not yet implemented'); -// self._visitingAlter = true; -// var table = self._queryNode.table; -// var result = ['EXEC sp_rename '+ -// self.visit(table.toNode())+'.'+self.visit(alter.nodes[0].nodes[0])+', '+ -// self.visit(alter.nodes[0].nodes[1])+', '+ -// "'COLUMN'" -// ]; -// self._visitingAlter = false; -// return result + self._visitingAlter = true; + var table = self._queryNode.table; + var result = ["EXEC sp_rename '"+ + self.visit(table.toNode())+'.'+self.visit(alter.nodes[0].nodes[0])+"', "+ + self.visit(alter.nodes[0].nodes[1])+', '+ + "'COLUMN'" + ]; + self._visitingAlter = false; + return result; } if (isAlterAddColumn(alter)) return _addColumn(); @@ -125,13 +141,13 @@ Mssql.prototype.visitAlter = function(alter) { // CASE WHEN true THEN xxx END // the "true" has to be a boolean expression like 1=1 Mssql.prototype.visitCase = function(caseExp) { - var _this=this + var _this=this; function _whenValue(node){ if (node.type!='PARAMETER') return _this.visit(node); // dealing with a true/false value var val=node.value(); - if (val==true) return '1=1'; else return '0=1'; + if (val===true) return '1=1'; else return '0=1'; } assert(caseExp.whenList.length == caseExp.thenList.length); @@ -147,7 +163,7 @@ Mssql.prototype.visitCase = function(caseExp) { text += whenExp + thenExp; } - if (null != caseExp.else && undefined != caseExp.else) { + if (null !== caseExp.else && undefined !== caseExp.else) { text += ' ELSE ' + this.visit(caseExp.else); } @@ -155,7 +171,7 @@ Mssql.prototype.visitCase = function(caseExp) { text += ' END)'; return [text]; -} +}; Mssql.prototype.visitColumn = function(columnNode) { var self=this; @@ -163,12 +179,12 @@ Mssql.prototype.visitColumn = function(columnNode) { var inSelectClause; function _arrayAgg(){ - throw new Error("SQL Server does not support array_agg.") + throw new Error("SQL Server does not support array_agg."); } function _countStar(){ // Implement our own since count(table.*) is invalid in Mssql - var result='COUNT(*)' + var result='COUNT(*)'; if(inSelectClause && columnNode.alias) { result += ' AS ' + self.quote(columnNode.alias); } @@ -178,14 +194,16 @@ Mssql.prototype.visitColumn = function(columnNode) { table = columnNode.table; inSelectClause = !this._selectOrDeleteEndIndex; if (isCountStarExpression(columnNode)) return _countStar(); - if (inSelectClause && !table.alias && columnNode.asArray) return _arrayAgg(); + if (inSelectClause && table && !table.alias && columnNode.asArray) return _arrayAgg(); return Mssql.super_.prototype.visitColumn.call(this, columnNode); }; Mssql.prototype.visitCreate = function(create) { - if (!isCreateIfNotExists(create)) { + var isNotExists=isCreateIfNotExists(create); + var isTemporary=isCreateTemporary(create); + if (!isNotExists && !isTemporary) { return Mssql.super_.prototype.visitCreate.call(this, create); } // Implement our own create if not exists: @@ -209,6 +227,7 @@ Mssql.prototype.visitCreate = function(create) { // if (schema) { whereClause+=' AND TABLE_SCHEMA = schemaResult.join(' ')} // Add some tests for this as well + if (!isNotExists) return createResult; return ['IF NOT EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES '+whereClause+') BEGIN '+createResult.join(' ')+' END']; }; @@ -233,16 +252,52 @@ Mssql.prototype.visitDrop = function(drop) { return ['IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES '+whereClause+') BEGIN '+dropResult.join(' ')+' END']; }; +Mssql.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 = 'DATEPART(' + functionCall.name.toLowerCase() + ', ' + (nodes[0]+'') + ')'; + return txt; + } + + var txt; + // Override date functions since mssql uses datepart + 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 { + 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; + 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; }; /** @@ -260,77 +315,63 @@ Mssql.prototype.visitOrderBy = function(orderBy) { * @returns {String[]} */ Mssql.prototype.visitQueryHelper=function(actions,targets,filters){ - /** - * - * @param {Node[]} list - * @param {String} type - * @returns {Object|undefined} {index:number, node:Node} - * @private - */ - function _findNode(list,type){ - for (var i= 0, len=list.length; 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; + + if (result[2] === 'DEFAULT VALUES') { + result[2] = '() VALUES ()'; + } + return result; +}; + Mysql.prototype._getParameterPlaceholder = function() { return '?'; }; @@ -31,6 +69,24 @@ Mysql.prototype._getParameterValue = function(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.'); }; @@ -85,6 +141,81 @@ Mysql.prototype.visitBinary = function(binary) { 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 04395d9c..9b81af25 100644 --- a/lib/dialect/postgres.js +++ b/lib/dialect/postgres.js @@ -6,9 +6,10 @@ var From = require('../node/from'); var Select = require('../node/select'); var Table = require('../table'); -var Postgres = function() { +var Postgres = function(config) { this.output = []; this.params = []; + this.config = config || {}; }; Postgres.prototype._myClass = Postgres; @@ -25,7 +26,7 @@ Postgres.prototype._getParameterText = function(index, value) { } }; -Postgres.prototype._getParameterValue = function(value) { +Postgres.prototype._getParameterValue = function(value, quoteChar) { // handle primitives if (null === value) { value = 'NULL'; @@ -35,22 +36,39 @@ Postgres.prototype._getParameterValue = function(value) { // number is just number value = value; } else if ('string' === typeof value) { - // string uses single quote - value = this.quote(value, "'"); + // string uses single quote by default + value = this.quote(value, quoteChar || "'"); } else if ('object' === typeof value) { - if (_.isArray(value)) { - // convert each element of the array - value = _.map(value, this._getParameterValue, this); - value = '(' + value.join(', ') + ')'; - } else if (_.isFunction(value.toISOString)) { + if (Array.isArray(value)) { + if (this._myClass === Postgres) { + // naive check to see if this is an array of objects, which + // is handled differently than an array of primitives + if (value.length && 'object' === typeof value[0] && + !_.isFunction(value[0].toISOString) && + !Array.isArray(value[0])) { + value = "'" + JSON.stringify(value) + "'"; + } else { + var self = this; + value = value.map(function (item) { + // In a Postgres array, strings must be double-quoted + return self._getParameterValue(item, '"'); + }); + value = '\'{' + value.join(',') + '}\''; + } + } else { + value = _.map(value, this._getParameterValue.bind(this)); + value = '(' + value.join(', ') + ')'; + } + } else if (value instanceof Date) { // Date object's default toString format does not get parsed well - // Handle date like objects using toISOString + // Handle dates using toISOString value = this._getParameterValue(value.toISOString()); } else if (Buffer.isBuffer(value)) { value = this._getParameterValue('\\x' + value.toString('hex')); } else { // rich object represent with string - value = this._getParameterValue(value.toString()); + var strValue = value.toString(); + value = strValue === '[object Object]' ? this._getParameterValue(JSON.stringify(value)) : this._getParameterValue(strValue); } } else { throw new Error('Unable to use ' + value + ' in query'); @@ -72,6 +90,16 @@ Postgres.prototype.getQuery = function(queryNode) { } this.output = this.visit(queryNode); + //if is a create view, must replace paramaters with values + if (this.output.indexOf('CREATE VIEW') > -1) { + var previousFlagStatus = this._disableParameterPlaceholders; + this._disableParameterPlaceholders = true; + this.output = []; + this.output = this.visit(queryNode); + this.params = []; + this._disableParameterPlaceholders = previousFlagStatus; + } + // create the query object var query = { text: this.output.join(' '), values: this.params }; @@ -103,10 +131,14 @@ Postgres.prototype.visit = function(node) { case 'SUBQUERY' : return this.visitSubquery(node); case 'SELECT' : return this.visitSelect(node); case 'INSERT' : return this.visitInsert(node); + case 'REPLACE' : return this.visitReplace(node); case 'UPDATE' : return this.visitUpdate(node); case 'DELETE' : return this.visitDelete(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); @@ -117,10 +149,13 @@ Postgres.prototype.visit = function(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; @@ -128,6 +163,7 @@ Postgres.prototype.visit = function(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); @@ -139,6 +175,8 @@ Postgres.prototype.visit = function(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); @@ -159,6 +197,8 @@ Postgres.prototype.visit = function(node) { }; Postgres.prototype._quoteCharacter = '"'; +Postgres.prototype._aliasText = ' AS '; + Postgres.prototype.quote = function(word, quoteCharacter) { var q; if (quoteCharacter) { @@ -169,15 +209,28 @@ Postgres.prototype.quote = function(word, quoteCharacter) { } // handle square brackets specially if (q=='['){ - return '['+word+']' + return '['+word+']'; } else { return q + word.replace(new RegExp(q,'g'),q+q) + q; } }; Postgres.prototype.visitSelect = function(select) { - var result = ['SELECT', select.nodes.map(this.visit.bind(this)).join(', ')]; + var result = ['SELECT']; + + if (select.isDistinct) result.push('DISTINCT'); + + var distinctOnNode = select.nodes.filter(function (node) {return node.type === 'DISTINCT ON';}).shift(); + var nonDistinctOnNodes = select.nodes.filter(function (node) {return node.type !== 'DISTINCT ON';}); + + if (distinctOnNode) { + result.push(this.visit(distinctOnNode)); + } + + result.push(nonDistinctOnNodes.map(this.visit.bind(this)).join(', ')); + this._selectOrDeleteEndIndex = this.output.length + result.length; + return result; }; @@ -186,11 +239,10 @@ Postgres.prototype.visitInsert = function(insert) { // don't use table.column for inserts this._visitedInsert = true; - var result = [ - 'INSERT INTO', - this.visit(this._queryNode.table.toNode()), - '(' + insert.columns.map(this.visit.bind(this)).join(', ') + ')' - ]; + 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(); @@ -215,6 +267,10 @@ Postgres.prototype.visitInsert = function(insert) { return result; }; +Postgres.prototype.visitReplace = function(replace) { + throw new Error('Postgres does not support REPLACE.'); +}; + Postgres.prototype.visitUpdate = function(update) { // don't auto-generate from clause var params = []; @@ -248,8 +304,10 @@ Postgres.prototype.visitCreate = function(create) { // don't auto-generate from clause var table = this._queryNode.table; var col_nodes = table.columns.map(function(col) { return col.toNode(); }); + var foreign_key_nodes = table.foreignKeys; - var result = ['CREATE TABLE']; + var result = ['CREATE TABLE']; + if (create.options.isTemporary) result=['CREATE TEMPORARY TABLE']; result = result.concat(create.nodes.map(this.visit.bind(this))); result.push(this.visit(table.toNode())); var primary_col_nodes = col_nodes.filter(function(n) { @@ -264,6 +322,9 @@ Postgres.prototype.visitCreate = function(create) { }.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; @@ -278,8 +339,23 @@ Postgres.prototype.visitDrop = function(drop) { return result; }; +Postgres.prototype.visitTruncate = function(truncate) { + var result = ['TRUNCATE TABLE']; + result = result.concat(truncate.nodes.map(this.visit.bind(this))); + return result; +}; + +Postgres.prototype.visitDistinct = function(truncate) { + // Nothing to do here since it's handled in the SELECT clause + return []; +}; + +Postgres.prototype.visitDistinctOn = function(distinctOn) { + return ['DISTINCT ON('+distinctOn.nodes.map(this.visit.bind(this)).join(', ')+')']; +}; + Postgres.prototype.visitAlias = function(alias) { - var result = [this.visit(alias.value) + ' AS ' + this.quote(alias.alias)]; + var result = [this.visit(alias.value) + this._aliasText + this.quote(alias.alias)]; return result; }; @@ -297,7 +373,9 @@ Postgres.prototype.visitAlter = function(alter) { }; Postgres.prototype.visitCast = function(cast) { + this._visitingCast = true; var result = ['CAST(' + this.visit(cast.value) + ' AS ' + cast.dataType + ')']; + this._visitingCast = false; return result; }; @@ -323,6 +401,9 @@ Postgres.prototype.visitWhere = function(where) { 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; }; @@ -356,6 +437,10 @@ Postgres.prototype.visitPostfixUnary = function(unary) { Postgres.prototype.visitBinary = function(binary) { var self = this; + + binary.left.property = binary.left.name; + binary.right.property = binary.right.name; + var text = '(' + this.visit(binary.left) + ' ' + binary.operator + ' '; if (Array.isArray(binary.right)) { text += '(' + binary.right.map(function (node) { @@ -483,7 +568,7 @@ Postgres.prototype.visitCase = function(caseExp) { text += whenExp + thenExp; } - if (null != caseExp.else && undefined != caseExp.else) { + if (null !== caseExp.else && undefined !== caseExp.else) { text += ' ELSE ' + this.visit(caseExp.else); } @@ -491,7 +576,7 @@ Postgres.prototype.visitCase = function(caseExp) { text += ' END)'; return [text]; -} +}; Postgres.prototype.visitAt = function(at) { var text = '(' + this.visit(at.value) + ')[' + this.visit(at.index) + ']'; @@ -523,11 +608,14 @@ Postgres.prototype.visitOverlap = function(overlap) { }; Postgres.prototype.visitQuery = function(queryNode) { + if (this._queryNode) return this.visitSubquery(queryNode,dontParenthesizeSubQuery(this._queryNode)); this._queryNode = queryNode; // need to sort the top level query nodes on visitation priority // so select/insert/update/delete comes before from comes before where var missingFrom = true; var hasFrom = false; + var createView; + var isSelect = false; var actions = []; var targets = []; var filters = []; @@ -535,14 +623,17 @@ Postgres.prototype.visitQuery = function(queryNode) { var node = queryNode.nodes[i]; switch(node.type) { case "SELECT": + isSelect = true; // jshint ignore:line case "DELETE": actions.push(node); break; case "INDEXES": case "INSERT": + case "REPLACE": case "UPDATE": case "CREATE": case "DROP": + case "TRUNCATE": case "ALTER": actions.push(node); missingFrom = false; @@ -553,6 +644,9 @@ Postgres.prototype.visitQuery = function(queryNode) { missingFrom = false; targets.push(node); break; + case "CREATE VIEW": + createView = node; + break; default: filters.push(node); break; @@ -561,11 +655,20 @@ Postgres.prototype.visitQuery = function(queryNode) { if(!actions.length) { // if no actions are given, guess it's a select actions.push(new Select().add('*')); + isSelect = true; } - if(missingFrom) { + if(missingFrom && queryNode.table instanceof Table) { + // the instanceof handles the situation where a sql.select(some expression) is used and there should be no FROM clause targets.push(new From().add(queryNode.table)); } - return this.visitQueryHelper(actions,targets,filters) + if (createView) { + if (isSelect) { + actions.unshift(createView); + } else { + throw new Error('Create View requires a Select.'); + } + } + return this.visitQueryHelper(actions,targets,filters); }; /** @@ -577,19 +680,20 @@ Postgres.prototype.visitQuery = function(queryNode) { * @returns {String[]} */ Postgres.prototype.visitQueryHelper=function(actions,targets,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' - return this.output; -} + 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' + return this.output; +}; -Postgres.prototype.visitSubquery = function(queryNode) { +Postgres.prototype.visitSubquery = function(queryNode,dontParenthesize) { // create another query builder of the current class to build the subquery - var subQuery = new this._myClass(); + var subQuery = new this._myClass(this.config); // let the subquery modify this instance's params array subQuery.params = this.params; @@ -605,7 +709,10 @@ Postgres.prototype.visitSubquery = function(queryNode) { } var alias = queryNode.alias; - return ['(' + subQuery.output.join(' ') + ')' + (alias ? ' ' + alias : '')]; + if (dontParenthesize) { + return [subQuery.output.join(' ') + (alias ? ' ' + this.quote(alias) : '')]; + } + return ['(' + subQuery.output.join(' ') + ')' + (alias ? ' ' + this.quote(alias) : '')]; }; Postgres.prototype.visitTable = function(tableNode) { @@ -616,28 +723,30 @@ Postgres.prototype.visitTable = function(tableNode) { txt += '.'; } txt += this.quote(table.getName()); - if(table.alias) { - txt += ' AS ' + this.quote(table.alias); + if(typeof table.alias === 'string') { + txt += this._aliasText + this.quote(table.alias); } return [txt]; }; Postgres.prototype.visitColumn = function(columnNode) { var table = columnNode.table; - var inInsertUpdateClause = this._visitedInsert || this._visitingUpdateTargetColumn; + var inInsertUpdateClause = this._visitedInsert || this._visitedReplace || this._visitingUpdateTargetColumn; var inDdlClause = this._visitingAddColumn || this._visitingAlter || this._visitingCreate; var inSelectClause = this.visitingReturning || (!this._selectOrDeleteEndIndex - && !this._visitingWhere - && !inInsertUpdateClause - && !inDdlClause - && !this.visitingCase - && !this._visitingJoin + && !this._visitingWhere // jshint ignore:line + && !inInsertUpdateClause // jshint ignore:line + && !inDdlClause // jshint ignore:line + && !this.visitingCase // jshint ignore:line + && !this._visitingJoin // jshint ignore:line ); + var inFunctionCall = this._visitingFunctionCall; + var inCast = this._visitingCast; var txt = []; var closeParen = 0; - if(inSelectClause && (!table.alias || !!columnNode.alias)) { + if(inSelectClause && (table && !table.alias || !!columnNode.alias)) { if (columnNode.asArray) { closeParen++; txt.push(this._arrayAggFunctionName+'('); @@ -653,17 +762,19 @@ Postgres.prototype.visitColumn = function(columnNode) { txt.push('DISTINCT('); } } - if(!inInsertUpdateClause && !this.visitingReturning && !this._visitingCreate && !this._visitingAlter) { - if(table.alias) { - txt.push(this.quote(table.alias)); - } else { - if(table.getSchema()) { - txt.push(this.quote(table.getSchema())); - txt.push('.'); + 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(this.quote(table.getName())); + txt.push('.'); } - txt.push('.'); } if (columnNode.star) { var allCols = []; @@ -674,7 +785,7 @@ Postgres.prototype.visitColumn = function(columnNode) { var col = table.columns[i]; var aliased = col.name !== (col.alias || col.property); hasAliases = hasAliases || aliased; - allCols.push(tableName + this.quote(col.name) + (aliased ? ' AS ' + this.quote(col.alias || col.property) : '')); + allCols.push(tableName + this.quote(col.name) + (aliased ? this._aliasText + this.quote(col.alias || col.property) : '')); } } if(hasAliases) { @@ -684,16 +795,27 @@ Postgres.prototype.visitColumn = function(columnNode) { txt.push('*'); } } + else if (columnNode.isConstant) { + // this injects directly into SELECT statement rather than creating a parameter + // txt.push(this._getParameterValue(columnNode.literalValue)) + // currently thinking it is better to generate a parameter + var value = columnNode.constantValue; + this.params.push(value); + txt.push(this._getParameterText(this.params.length, value)); + } else { + if (columnNode.subfieldContainer) { + txt.push('(' + this.visitColumn(columnNode.subfieldContainer) + ').'); + } txt.push(this.quote(columnNode.name)); } if(closeParen) { - for(var i = 0; i < closeParen; i++) { + for(var j = 0; j < closeParen; j++) { txt.push(')'); } } - if(inSelectClause && (columnNode.alias || columnNode.property !== columnNode.name)) { - txt.push(' AS ' + this.quote(columnNode.alias || columnNode.property)); + 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 + @@ -707,6 +829,12 @@ Postgres.prototype.visitColumn = function(columnNode) { } 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) { @@ -725,16 +853,107 @@ Postgres.prototype.visitColumn = function(columnNode) { columnNode.name + ' (REFERENCES statements within CREATE TABLE and ADD COLUMN statements' + ' require a table and column)'); - txt.push(' REFERENCES ' + columnNode.references.table + '(' + - columnNode.references.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) { - var txt = functionCall.name + '(' + functionCall.nodes.map(this.visit.bind(this)).join(', ') + ')'; + this._visitingFunctionCall = true; + var _this = this; + + function _extract() { + var nodes = functionCall.nodes.map(_this.visit.bind(_this)); + if (nodes.length != 1) throw new Error('Not enough parameters passed to ' + functionCall.name + ' function'); + var txt = 'EXTRACT(' + functionCall.name + ' FROM ' + (nodes[0]+'') + ')'; + return txt; + } + + var txt = ""; + // Override date functions since postgres (and others) uses extract + if (['YEAR', 'MONTH', 'DAY', 'HOUR'].indexOf(functionCall.name) >= 0) txt = _extract(); + // Override CURRENT_TIMESTAMP function to remove parens + else if ('CURRENT_TIMESTAMP' == functionCall.name) txt = functionCall.name; + else txt = functionCall.name + '(' + functionCall.nodes.map(this.visit.bind(this)).join(', ') + ')'; + this._visitingFunctionCall = false; return [txt]; }; @@ -782,6 +1001,10 @@ 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']; }; @@ -799,6 +1022,7 @@ Postgres.prototype.visitForShare = function() { }; 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)); @@ -809,10 +1033,20 @@ Postgres.prototype.visitJoin = function(join) { return result; }; +Postgres.prototype.visitLeftJoinLateral = function(join) { + var result = []; + this._visitingJoin = true; + result = result.concat(this.visit(join.from)); + result = result.concat('LEFT JOIN LATERAL'); + result = result.concat(this.visit(join.to)); + result = result.concat('ON true'); + return result; +}; + Postgres.prototype.visitLiteral = function(node) { var txt = [node.literal]; if(node.alias) { - txt.push(' AS ' + this.quote(node.alias)); + txt.push(this._aliasText + this.quote(node.alias)); } return [txt.join('')]; }; @@ -825,6 +1059,41 @@ Postgres.prototype.visitReturning = function(returning) { 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); } @@ -34,10 +75,87 @@ 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.'); }; @@ -78,6 +196,10 @@ Sqlite.prototype.visitBinary = function(binary) { return ret; } return Sqlite.super_.prototype.visitBinary.call(this, binary); -} +}; + +Sqlite.prototype.visitOrIgnore = function() { + return ['OR IGNORE']; +}; module.exports = Sqlite; diff --git a/lib/functions.js b/lib/functions.js index 084eaa4c..fe9cdaaf 100644 --- a/lib/functions.js +++ b/lib/functions.js @@ -1,22 +1,23 @@ 'use strict'; var _ = require('lodash'); var sliced = require('sliced'); -var FunctionCall = require(__dirname + '/node/functionCall'); +var FunctionCall = require('./node/functionCall'); // create a function that creates a function call of the specific name, using the specified sql instance -var getFunctionCallCreator = function(name, sql) { +var getFunctionCallCreator = function(name) { return function() { // turn array-like arguments object into a true array - var functionCall = new FunctionCall(name, sliced(arguments)); - functionCall.sql = sql; - return functionCall; + return new FunctionCall(name, sliced(arguments)); }; }; // creates a hash of functions for a sql instance -var getFunctions = function(functionNames, sql) { +var getFunctions = function(functionNames) { + if (typeof functionNames === 'string') + return getFunctionCallCreator(functionNames); + var functions = _.reduce(functionNames, function(reducer, name) { - reducer[name] = getFunctionCallCreator(name, sql); + reducer[name] = getFunctionCallCreator(name); return reducer; }, {}); return functions; @@ -36,10 +37,12 @@ var aggregateFunctions = [ var scalarFunctions = [ 'ABS', 'COALESCE', + 'LEFT', 'LENGTH', 'LOWER', 'LTRIM', 'RANDOM', + 'RIGHT', 'ROUND', 'RTRIM', 'SUBSTR', @@ -47,17 +50,26 @@ var scalarFunctions = [ '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); +var standardFunctionNames = aggregateFunctions.concat(scalarFunctions).concat(hstoreFunction).concat(textsearchFunctions).concat(dateFunctions); // creates a hash of standard functions for a sql instance -var getStandardFunctions = function(sql) { - return getFunctions(standardFunctionNames, sql); +var getStandardFunctions = function() { + return getFunctions(standardFunctionNames); }; +module.exports.getFunctions = getFunctions; module.exports.getStandardFunctions = getStandardFunctions; diff --git a/lib/index.js b/lib/index.js index 75d1c2e7..f26ad827 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,22 +1,26 @@ 'use strict'; var _ = require('lodash'); +var Column = require("./column"); var FunctionCall = require('./node/functionCall'); var ArrayCall = require('./node/arrayCall'); var functions = require('./functions'); var getDialect = require('./dialect'); +var JoinNode = require('./node/join'); var Query = require('./node/query'); var sliced = require('sliced'); var Table = require('./table'); +var Interval = require('./node/interval'); // default dialect is postgres var DEFAULT_DIALECT = 'postgres'; -var Sql = function(dialect) { - this.setDialect(dialect || DEFAULT_DIALECT); +var Sql = function(dialect, config) { + this.setDialect(dialect || DEFAULT_DIALECT, config); // attach the standard SQL functions to this instance - this.functions = functions.getStandardFunctions(this); + this.functions = functions.getStandardFunctions(); + this.function = functions.getFunctions; }; // Define a table @@ -30,11 +34,8 @@ Sql.prototype.define = function(def) { // Returns a function call creator Sql.prototype.functionCallCreator = function(name) { - var sql = this; return function() { - var functionCall = new FunctionCall(name, sliced(arguments)); - functionCall.sql = sql; - return functionCall; + return new FunctionCall(name, sliced(arguments)); }; }; @@ -52,20 +53,52 @@ Sql.prototype.select = function() { return query; }; +// Returns a subQuery clause +Sql.prototype.subQuery = function(alias) { + // create the query and pass it off + var query = new Query(this); + query.type = 'SUBQUERY'; + query.alias = alias; + query.join = function(other) { + return new JoinNode('INNER', this.toNode(), other.toNode(), other); + }; + return query; +}; + +// Returns an interval clause +Sql.prototype.interval = function() { + var interval = new Interval(sliced(arguments)); + return interval; +}; + // Set the dialect -Sql.prototype.setDialect = function(dialect) { +Sql.prototype.setDialect = function(dialect, config) { this.dialect = getDialect(dialect); this.dialectName = dialect; + this.config = config; return this; }; +// Create a constant Column (for use in SELECT) +Sql.prototype.constant = function(value) { + var config={ + name:"constant", + property:"constant", + isConstant:true, + constantValue:value, + }; + var cn = new Column(config); + return cn; +}; + + // back compat shim for the Sql class constructor -var create = function(dialect) { - return new Sql(dialect); +var create = function(dialect, config) { + return new Sql(dialect, {}); }; -module.exports = new Sql(DEFAULT_DIALECT); +module.exports = new Sql(DEFAULT_DIALECT, {}); module.exports.create = create; module.exports.Sql = Sql; module.exports.Table = Table; diff --git a/lib/node/addColumn.js b/lib/node/addColumn.js index 273d8836..3d7d4066 100644 --- a/lib/node/addColumn.js +++ b/lib/node/addColumn.js @@ -1,6 +1,6 @@ 'use strict'; -var Node = require(__dirname); +var Node = require('./index'); module.exports = Node.define({ type: 'ADD COLUMN' diff --git a/lib/node/alias.js b/lib/node/alias.js index 78f01323..9f9855f5 100644 --- a/lib/node/alias.js +++ b/lib/node/alias.js @@ -1,7 +1,7 @@ 'use strict'; var _ = require('lodash'); -var Node = require(__dirname); +var Node = require('./index'); var AliasNode = Node.define({ type: 'ALIAS', diff --git a/lib/node/alter.js b/lib/node/alter.js index ce53623c..4aae4d83 100644 --- a/lib/node/alter.js +++ b/lib/node/alter.js @@ -1,6 +1,6 @@ 'use strict'; -var Node = require(__dirname); +var Node = require('./index'); module.exports = Node.define({ type: 'ALTER' diff --git a/lib/node/arrayCall.js b/lib/node/arrayCall.js index 4f2f657b..7533190d 100644 --- a/lib/node/arrayCall.js +++ b/lib/node/arrayCall.js @@ -1,9 +1,9 @@ 'use strict'; var _ = require('lodash'); -var Node = require(__dirname); -var ParameterNode = require(__dirname + '/parameter'); -var valueExpressionMixin = require(__dirname + '/valueExpression'); +var Node = require('./index'); +var ParameterNode = require('./parameter'); +var valueExpressionMixin = require('./valueExpression'); var ArrayCallNode = Node.define({ type: 'ARRAY CALL', @@ -18,7 +18,7 @@ var ArrayCallNode = Node.define({ _.extend(ArrayCallNode.prototype, valueExpressionMixin()); // allow aliasing -var AliasNode = require(__dirname + '/alias'); +var AliasNode = require('./alias'); _.extend(ArrayCallNode.prototype, AliasNode.AliasMixin); module.exports = ArrayCallNode; diff --git a/lib/node/at.js b/lib/node/at.js index 2ded9362..5e8ca47b 100644 --- a/lib/node/at.js +++ b/lib/node/at.js @@ -1,8 +1,8 @@ 'use strict'; var _ = require('lodash'); -var Node = require(__dirname); -var valueExpressionMixin = require(__dirname + '/valueExpression'); +var Node = require('./index'); +var valueExpressionMixin = require('./valueExpression'); var valueExpressionMixed = false; var AtNode = Node.define({ @@ -21,7 +21,7 @@ var AtNode = Node.define({ }); // allow aliasing -var AliasNode = require(__dirname + '/alias'); +var AliasNode = require('./alias'); _.extend(AtNode.prototype, AliasNode.AliasMixin); module.exports = AtNode; diff --git a/lib/node/binary.js b/lib/node/binary.js index 7b721650..da8eb8bd 100644 --- a/lib/node/binary.js +++ b/lib/node/binary.js @@ -1,8 +1,8 @@ 'use strict'; var _ = require('lodash'); -var Node = require(__dirname); -var valueExpressionMixin = require(__dirname + '/valueExpression'); +var Node = require('./index'); +var valueExpressionMixin = require('./valueExpression'); var valueExpressionMixed = false; var BinaryNode = Node.define(_.extend({ @@ -23,7 +23,7 @@ var BinaryNode = Node.define(_.extend({ })); // allow aliasing -var AliasNode = require(__dirname + '/alias'); +var AliasNode = require('./alias'); _.extend(BinaryNode.prototype, AliasNode.AliasMixin); module.exports = BinaryNode; diff --git a/lib/node/cascade.js b/lib/node/cascade.js index 1dea1eef..4b6646fd 100644 --- a/lib/node/cascade.js +++ b/lib/node/cascade.js @@ -1,6 +1,6 @@ 'use strict'; -var Node = require(__dirname); +var Node = require('./index'); module.exports = Node.define({ type: 'CASCADE' diff --git a/lib/node/case.js b/lib/node/case.js index b8729314..ac3bf214 100644 --- a/lib/node/case.js +++ b/lib/node/case.js @@ -1,8 +1,8 @@ 'use strict'; var _ = require('lodash'); -var Node = require(__dirname); -var valueExpressionMixin = require(__dirname + '/valueExpression'); +var Node = require('./index'); +var valueExpressionMixin = require('./valueExpression'); var valueExpressionMixed = false; var CaseNode = Node.define(_.extend({ @@ -23,7 +23,7 @@ var CaseNode = Node.define(_.extend({ })); // allow aliasing -var AliasNode = require(__dirname + '/alias'); +var AliasNode = require('./alias'); _.extend(CaseNode.prototype, AliasNode.AliasMixin); module.exports = CaseNode; diff --git a/lib/node/cast.js b/lib/node/cast.js index 60966ea9..be915b1e 100644 --- a/lib/node/cast.js +++ b/lib/node/cast.js @@ -1,8 +1,8 @@ 'use strict'; var _ = require('lodash'); -var Node = require(__dirname); -var valueExpressionMixin = require(__dirname + '/valueExpression'); +var Node = require('./index'); +var valueExpressionMixin = require('./valueExpression'); var valueExpressionMixed = false; var CastNode = Node.define({ @@ -21,7 +21,7 @@ var CastNode = Node.define({ }); // allow aliasing -var AliasNode = require(__dirname + '/alias'); +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 6234b2f2..6b145d74 100644 --- a/lib/node/column.js +++ b/lib/node/column.js @@ -1,6 +1,6 @@ 'use strict'; -var Node = require(__dirname); +var Node = require('./index'); module.exports = Node.define({ type: 'COLUMN', @@ -10,6 +10,8 @@ module.exports = Node.define({ 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; @@ -18,8 +20,14 @@ module.exports = Node.define({ this.distinct = config.distinct; this.primaryKey = config.primaryKey; this.notNull = config.notNull; + this.defaultValue = config.defaultValue; this.references = config.references; + // If subfieldContainer is present, this is a subfield and subfieldContainer + // is the parent Column + this.subfieldContainer = config.subfieldContainer; + this.subfields = config.subfields; this.autoGenerated = !!config.autoGenerated; + this.unique = !!config.unique; }, as: function(alias) { this.alias = alias; diff --git a/lib/node/create.js b/lib/node/create.js index bf535f6a..67cd9b3d 100644 --- a/lib/node/create.js +++ b/lib/node/create.js @@ -1,7 +1,14 @@ 'use strict'; -var Node = require(__dirname); +var Node = require('./index'); module.exports = Node.define({ - type: 'CREATE' + type: 'CREATE', + + constructor: function(isTemporary) { + Node.call(this); + + this.options = { isTemporary: isTemporary}; + }, + }); diff --git a/lib/node/createIndex.js b/lib/node/createIndex.js index 2384ab8e..8f8f0d04 100644 --- a/lib/node/createIndex.js +++ b/lib/node/createIndex.js @@ -49,7 +49,8 @@ module.exports = Node.define({ if (!result) { var columns = this.options.columns.map(function(col) { - return col.name; + var column = col.name ? col.name : col.value.name; + return column; }).sort(); result = [this.table._name]; diff --git a/lib/node/createView.js b/lib/node/createView.js new file mode 100644 index 00000000..7d456d90 --- /dev/null +++ b/lib/node/createView.js @@ -0,0 +1,13 @@ +'use strict'; + +var Node = require('./index'); + +module.exports = Node.define({ + type: 'CREATE VIEW', + + constructor: function(viewName) { + Node.call(this); + + this.options = { viewName: viewName}; + } +}); diff --git a/lib/node/default.js b/lib/node/default.js index 49bd7ebd..b5adc894 100644 --- a/lib/node/default.js +++ b/lib/node/default.js @@ -1,6 +1,6 @@ 'use strict'; -module.exports = require(__dirname).define({ +module.exports = require('./index').define({ type: 'DEFAULT', value: function() { return; diff --git a/lib/node/delete.js b/lib/node/delete.js index 2acb5872..0d02ebab 100644 --- a/lib/node/delete.js +++ b/lib/node/delete.js @@ -1,6 +1,6 @@ 'use strict'; -var Node = require(__dirname); +var Node = require('./index'); module.exports = Node.define({ type: 'DELETE' diff --git a/lib/node/distinct.js b/lib/node/distinct.js new file mode 100644 index 00000000..8c681551 --- /dev/null +++ b/lib/node/distinct.js @@ -0,0 +1,7 @@ +'use strict'; + +var Node = require('./index'); + +module.exports = Node.define({ + type: 'DISTINCT', +}); diff --git a/lib/node/distinctOn.js b/lib/node/distinctOn.js new file mode 100644 index 00000000..72c430e8 --- /dev/null +++ b/lib/node/distinctOn.js @@ -0,0 +1,7 @@ +'use strict'; + +var Node = require('./index'); + +module.exports = Node.define({ + type: 'DISTINCT ON', +}); diff --git a/lib/node/drop.js b/lib/node/drop.js index aa37b3ed..b0428347 100644 --- a/lib/node/drop.js +++ b/lib/node/drop.js @@ -1,12 +1,12 @@ 'use strict'; -var Node = require(__dirname); +var Node = require('./index'); module.exports = Node.define({ type: 'DROP', constructor: function(table) { - Node.call(this); - this.add(table); + Node.call(this); + this.add(table); } }); diff --git a/lib/node/dropColumn.js b/lib/node/dropColumn.js index 60ad4219..01fe36c5 100644 --- a/lib/node/dropColumn.js +++ b/lib/node/dropColumn.js @@ -1,6 +1,6 @@ 'use strict'; -var Node = require(__dirname); +var Node = require('./index'); module.exports = Node.define({ type: 'DROP COLUMN' diff --git a/lib/node/forShare.js b/lib/node/forShare.js index 328a4ebe..68a83678 100644 --- a/lib/node/forShare.js +++ b/lib/node/forShare.js @@ -1,6 +1,6 @@ 'use strict'; -var Node = require(__dirname); +var Node = require('./index'); module.exports = Node.define({ type: 'FOR SHARE' diff --git a/lib/node/forUpdate.js b/lib/node/forUpdate.js index c869f981..da4a2518 100644 --- a/lib/node/forUpdate.js +++ b/lib/node/forUpdate.js @@ -1,6 +1,6 @@ 'use strict'; -var Node = require(__dirname); +var Node = require('./index'); module.exports = Node.define({ type: 'FOR UPDATE' diff --git a/lib/node/foreignKey.js b/lib/node/foreignKey.js new file mode 100644 index 00000000..a0ad99c1 --- /dev/null +++ b/lib/node/foreignKey.js @@ -0,0 +1,19 @@ +'use strict'; + +var Node = require('./index'); + +module.exports = Node.define({ + type: 'FOREIGN KEY', + constructor: function(config) { + Node.call(this); + this.name = config.name; + this.columns = config.columns; + this.schema = config.schema; + this.table = config.table; + this.refColumns = config.refColumns; + this.onUpdate = config.onUpdate; + this.onDelete = config.onDelete; + this.constraint = config.constraint; + } +}); + diff --git a/lib/node/from.js b/lib/node/from.js index 84a6010f..12b54565 100644 --- a/lib/node/from.js +++ b/lib/node/from.js @@ -1,6 +1,6 @@ 'use strict'; -var Node = require(__dirname); +var Node = require('./index'); var From = Node.define({ type: 'FROM' diff --git a/lib/node/functionCall.js b/lib/node/functionCall.js index 6d43c279..608a3c1e 100644 --- a/lib/node/functionCall.js +++ b/lib/node/functionCall.js @@ -1,9 +1,9 @@ 'use strict'; var _ = require('lodash'); -var Node = require(__dirname); -var ParameterNode = require(__dirname + '/parameter'); -var valueExpressionMixin = require(__dirname + '/valueExpression'); +var Node = require('./index'); +var ParameterNode = require('./parameter'); +var valueExpressionMixin = require('./valueExpression'); var FunctionCallNode = Node.define({ type: 'FUNCTION CALL', @@ -18,7 +18,7 @@ var FunctionCallNode = Node.define({ _.extend(FunctionCallNode.prototype, valueExpressionMixin()); // allow aliasing -var AliasNode = require(__dirname + '/alias'); +var AliasNode = require('./alias'); _.extend(FunctionCallNode.prototype, AliasNode.AliasMixin); module.exports = FunctionCallNode; diff --git a/lib/node/groupBy.js b/lib/node/groupBy.js index 6e5888d1..53f63e94 100644 --- a/lib/node/groupBy.js +++ b/lib/node/groupBy.js @@ -1,6 +1,6 @@ 'use strict'; -var Node = require(__dirname); +var Node = require('./index'); module.exports = Node.define({ type: 'GROUP BY' diff --git a/lib/node/having.js b/lib/node/having.js index 7dcf3434..6f1523ac 100644 --- a/lib/node/having.js +++ b/lib/node/having.js @@ -1,6 +1,6 @@ 'use strict'; -var Node = require(__dirname); +var Node = require('./index'); module.exports = Node.define({ type: 'HAVING' diff --git a/lib/node/ifExists.js b/lib/node/ifExists.js index 9b759779..c26df660 100644 --- a/lib/node/ifExists.js +++ b/lib/node/ifExists.js @@ -1,6 +1,6 @@ 'use strict'; -var Node = require(__dirname); +var Node = require('./index'); module.exports = Node.define({ type: 'IF EXISTS' diff --git a/lib/node/ifNotExists.js b/lib/node/ifNotExists.js index ef414551..d731ccb2 100644 --- a/lib/node/ifNotExists.js +++ b/lib/node/ifNotExists.js @@ -1,6 +1,6 @@ 'use strict'; -var Node = require(__dirname); +var Node = require('./index'); module.exports = Node.define({ type: 'IF NOT EXISTS' diff --git a/lib/node/in.js b/lib/node/in.js index 4c807331..233b7c28 100644 --- a/lib/node/in.js +++ b/lib/node/in.js @@ -1,8 +1,8 @@ 'use strict'; var _ = require('lodash'); -var Node = require(__dirname); -var valueExpressionMixin = require(__dirname + '/valueExpression'); +var Node = require('./index'); +var valueExpressionMixin = require('./valueExpression'); var valueExpressionMixed = false; var InNode = Node.define(_.extend({ @@ -22,7 +22,7 @@ var InNode = Node.define(_.extend({ })); // allow aliasing -var AliasNode = require(__dirname + '/alias'); +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 e0dabff5..66c0e481 100644 --- a/lib/node/index.js +++ b/lib/node/index.js @@ -15,7 +15,11 @@ Node.prototype.toNode = function() { Node.prototype.add = function(node) { assert(node, 'Error while trying to add a non-existant node to a query'); - this.nodes.push(typeof node === 'string' ? new TextNode(node) : node.toNode()); + var newNode; + if (typeof node === 'string') newNode = new TextNode(node); + else if (node.toNode) newNode = node.toNode(); + else newNode = node; + this.nodes.push(newNode); return this; }; @@ -45,9 +49,14 @@ var determineDialect = function(query, dialect) { return Dialect; }; +var initializeDialect = function(Dialect, query) { + var config = query.sql ? query.sql.config : {}; + return new Dialect(config); +}; + Node.prototype.toQuery = function(dialect) { var Dialect = determineDialect(this, dialect); - return new Dialect().getQuery(this); + return initializeDialect(Dialect, this).getQuery(this); }; Node.prototype.toNamedQuery = function(name, dialect) { @@ -61,7 +70,7 @@ Node.prototype.toNamedQuery = function(name, dialect) { Node.prototype.toString = function(dialect) { var Dialect = determineDialect(this, dialect); - return new Dialect().getString(this); + return initializeDialect(Dialect, this).getString(this); }; Node.prototype.addAll = function(nodes) { @@ -89,4 +98,4 @@ Node.define = function(def) { }; module.exports = Node; -var TextNode = require(__dirname + '/text'); +var TextNode = require('./text'); 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 7186f694..f569997e 100644 --- a/lib/node/join.js +++ b/lib/node/join.js @@ -1,6 +1,6 @@ 'use strict'; -var Node = require(__dirname); +var Node = require('./index'); var JoinNode = module.exports = Node.define({ type: 'JOIN', constructor: function(subType, from, to) { @@ -19,5 +19,8 @@ var JoinNode = module.exports = Node.define({ }, leftJoin: function(other) { return new JoinNode('LEFT', this, other); + }, + leftJoinLateral: function(other) { + return new JoinNode('LEFT LATERAL', this, other); } }); diff --git a/lib/node/literal.js b/lib/node/literal.js index c4f539bb..f5a1ea58 100644 --- a/lib/node/literal.js +++ b/lib/node/literal.js @@ -1,6 +1,6 @@ 'use strict'; -var Node = require(__dirname); +var Node = require('./index'); module.exports = Node.define({ type: 'LITERAL', diff --git a/lib/node/notIn.js b/lib/node/notIn.js index e363f7f3..c50976c3 100644 --- a/lib/node/notIn.js +++ b/lib/node/notIn.js @@ -1,8 +1,8 @@ 'use strict'; var _ = require('lodash'); -var Node = require(__dirname); -var valueExpressionMixin = require(__dirname + '/valueExpression'); +var Node = require('./index'); +var valueExpressionMixin = require('./valueExpression'); var valueExpressionMixed = false; var NotInNode = Node.define(_.extend({ @@ -22,7 +22,7 @@ var NotInNode = Node.define(_.extend({ })); // allow aliasing -var AliasNode = require(__dirname + '/alias'); +var AliasNode = require('./alias'); _.extend(NotInNode.prototype, AliasNode.AliasMixin); module.exports = NotInNode; diff --git a/lib/node/onConflict.js b/lib/node/onConflict.js new file mode 100644 index 00000000..cc71ac47 --- /dev/null +++ b/lib/node/onConflict.js @@ -0,0 +1,7 @@ +'use strict'; + +var Node = require('./index'); + +module.exports = Node.define({ + type: 'ONCONFLICT' +}); diff --git a/lib/node/onDuplicate.js b/lib/node/onDuplicate.js new file mode 100644 index 00000000..cd653cb3 --- /dev/null +++ b/lib/node/onDuplicate.js @@ -0,0 +1,7 @@ +'use strict'; + +var Node = require('./index'); + +module.exports = Node.define({ + type: 'ONDUPLICATE' +}); diff --git a/lib/node/orIgnore.js b/lib/node/orIgnore.js new file mode 100644 index 00000000..e23c9d6d --- /dev/null +++ b/lib/node/orIgnore.js @@ -0,0 +1,7 @@ +'use strict'; + +var Node = require('./index'); + +module.exports = Node.define({ + type: 'OR IGNORE' +}); diff --git a/lib/node/orderBy.js b/lib/node/orderBy.js index 6db2a978..b97fb891 100644 --- a/lib/node/orderBy.js +++ b/lib/node/orderBy.js @@ -1,6 +1,6 @@ 'use strict'; -var Node = require(__dirname); +var Node = require('./index'); module.exports = Node.define({ type: 'ORDER BY' diff --git a/lib/node/orderByValue.js b/lib/node/orderByValue.js index 51d44567..47a8d5c0 100644 --- a/lib/node/orderByValue.js +++ b/lib/node/orderByValue.js @@ -1,6 +1,6 @@ 'use strict'; -var Node = require(__dirname); +var Node = require('./index'); var OrderByColumn = Node.define({ type: 'ORDER BY VALUE', diff --git a/lib/node/parameter.js b/lib/node/parameter.js index 9d825d42..051ed852 100644 --- a/lib/node/parameter.js +++ b/lib/node/parameter.js @@ -1,13 +1,13 @@ 'use strict'; -var Node = require(__dirname); +var Node = require('./index'); var ParameterNode = module.exports = Node.define({ type: 'PARAMETER', constructor: function(val) { Node.call(this); this._val = val; - this.isExplicit = false; + this.isExplicit = false; }, value: function() { return this._val; diff --git a/lib/node/postfixUnary.js b/lib/node/postfixUnary.js index 0c132f8a..8c66b89f 100644 --- a/lib/node/postfixUnary.js +++ b/lib/node/postfixUnary.js @@ -1,8 +1,8 @@ 'use strict'; var _ = require('lodash'); -var Node = require(__dirname); -var valueExpressionMixin = require(__dirname + '/valueExpression'); +var Node = require('./index'); +var valueExpressionMixin = require('./valueExpression'); var valueExpressionMixed = false; var PostfixUnaryNode = Node.define({ @@ -22,7 +22,7 @@ var PostfixUnaryNode = Node.define({ }); // allow aliasing -var AliasNode = require(__dirname + '/alias'); +var AliasNode = require('./alias'); _.extend(PostfixUnaryNode.prototype, AliasNode.AliasMixin); module.exports = PostfixUnaryNode; diff --git a/lib/node/prefixUnary.js b/lib/node/prefixUnary.js index 67a59fa1..fbde69c0 100644 --- a/lib/node/prefixUnary.js +++ b/lib/node/prefixUnary.js @@ -1,8 +1,8 @@ 'use strict'; var _ = require('lodash'); -var Node = require(__dirname); -var valueExpressionMixin = require(__dirname + '/valueExpression'); +var Node = require('./index'); +var valueExpressionMixin = require('./valueExpression'); var valueExpressionMixed = false; var PrefixUnaryNode = Node.define({ @@ -22,7 +22,7 @@ var PrefixUnaryNode = Node.define({ }); // allow aliasing -var AliasNode = require(__dirname + '/alias'); +var AliasNode = require('./alias'); _.extend(PrefixUnaryNode.prototype, AliasNode.AliasMixin); module.exports = PrefixUnaryNode; diff --git a/lib/node/query.js b/lib/node/query.js index 255332fd..ebb3ecfa 100644 --- a/lib/node/query.js +++ b/lib/node/query.js @@ -1,8 +1,11 @@ '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'); @@ -12,13 +15,19 @@ 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'); @@ -29,12 +38,15 @@ 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) { @@ -123,6 +135,11 @@ var Query = Node.define({ return this; }, + leftJoin: function(other) { + assert(this.type === 'SUBQUERY', 'leftJoin() can only be used on a subQuery'); + return new JoinNode('LEFT', this, other.toNode()); + }, + where: function(node) { if (arguments.length > 1) { // allow multiple where clause arguments @@ -156,6 +173,9 @@ var Query = Node.define({ order: function() { var args = getArrayOrArgsAsArray(arguments); var orderBy; + if (args.length === 0) { + return this; + } if (this._orderBy) { orderBy = this._orderBy; } else { @@ -208,6 +228,36 @@ var Query = Node.define({ }, + 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(); @@ -222,10 +272,10 @@ var Query = Node.define({ }, parameter: function(v) { - var param = ParameterNode.getNodeOrParameterNode(v); - param.isExplicit = true; - return this.add(param); - }, + var param = ParameterNode.getNodeOrParameterNode(v); + param.isExplicit = true; + return this.add(param); + }, delete: function(params) { var result; @@ -252,12 +302,39 @@ var Query = Node.define({ }, returning: function() { - var args = getArrayOrArgsAsArray(arguments); var returning = new Returning(); - returning.addAll(args); + if (arguments.length === 0) + returning.add('*'); + else + returning.addAll(getArrayOrArgsAsArray(arguments)); + return this.add(returning); }, + onDuplicate: function(o) { + var self = this; + + var onDuplicate = new OnDuplicate(); + Object.keys(o).forEach(function(key) { + var col = self.table.get(key); + if(col && !col.autoGenerated) + var val = o[key]; + onDuplicate.add(col.value(ParameterNode.getNodeOrParameterNode(val))); // jshint ignore:line + }); + + return self.add(onDuplicate); + }, + + onConflict: function(o) { + var self = this; + + var onConflict = new OnConflict(); + Object.keys(o).forEach(function(key) { + onConflict[key] = o[key]; + }); + return self.add(onConflict); + }, + forUpdate: function() { assert(typeof this._select !== 'undefined', 'FOR UPDATE can be used only in a select statement'); this.add(new ForUpdate()); @@ -276,7 +353,7 @@ var Query = Node.define({ this.add(createIndex); return createIndex; } else { - return this.add(new Create()); + return this.add(new Create(this.table.isTemporary)); } }, @@ -291,6 +368,41 @@ var Query = Node.define({ } }, + 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()); }, @@ -391,6 +503,11 @@ var Query = Node.define({ return this; }, + orIgnore: function() { + this.nodes[0].unshift(new OrIgnore()); + return this; + }, + cascade: function() { this.nodes[0].add(new Cascade()); return this; @@ -406,7 +523,30 @@ var Query = Node.define({ table: this.table }); return this.add(this.indexesClause); + }, + + createView: function(viewName) { + this.add(new CreateView(viewName)); + return this; } }); +// Here we are extending query with valueExpressions so that it's possible to write queries like +// var query=sql.select(a.select(a.x.sum()).plus(b.select(b.y.sum())) +// which generates: +// SELECT (SELECT SUM(a.x) FROM a) + (SELECT SUM(b.y) FROM b) +// We need to remove "or" and "and" from here because it conflicts with the already existing functionality of appending +// to the where clause like so: +// var query=a.select().where(a.name.equals("joe")).or(a.name.equals("sam")) +var valueExpressions=valueExpressionMixin(); +delete valueExpressions.or; +delete valueExpressions.and; +_.extend(Query.prototype, valueExpressions); + +// Extend the query with the aliasMixin so that it's possible to write queries like +// var query=sql.select(a.select(a.count()).as("column1")) +// which generates: +// SELECT (SELECT COUNT(*) FROM a) AS "column1" +_.extend(Query.prototype, alias.AliasMixin); + module.exports = Query; diff --git a/lib/node/rename.js b/lib/node/rename.js index 9767a528..c87bcfb3 100644 --- a/lib/node/rename.js +++ b/lib/node/rename.js @@ -1,6 +1,6 @@ 'use strict'; -var Node = require(__dirname); +var Node = require('./index'); module.exports = Node.define({ type: 'RENAME' diff --git a/lib/node/renameColumn.js b/lib/node/renameColumn.js index f886a11d..c3c66865 100644 --- a/lib/node/renameColumn.js +++ b/lib/node/renameColumn.js @@ -1,6 +1,6 @@ 'use strict'; -var Node = require(__dirname); +var Node = require('./index'); module.exports = Node.define({ type: 'RENAME COLUMN' diff --git a/lib/node/replace.js b/lib/node/replace.js new file mode 100644 index 00000000..d17099d2 --- /dev/null +++ b/lib/node/replace.js @@ -0,0 +1,70 @@ +'use strict'; + +var DefaultNode = require('./default'); +var Node = require('./'); +var ParameterNode = require('./parameter'); + +var Replace = Node.define({ + type: 'REPLACE', + constructor: function () { + Node.call(this); + this.names = []; + this.columns = []; + this.valueSets = []; + } +}); + +module.exports = Replace; + +Replace.prototype.add = function (nodes) { + var hasColumns = false; + var hasValues = false; + var self = this; + var values = {}; + nodes.forEach(function (node) { + var column = node.toNode(); + var name = column.name; + var idx = self.names.indexOf(name); + if (idx < 0) { + self.names.push(name); + self.columns.push(column); + } + hasColumns = true; + hasValues = hasValues || column.value !== undefined; + values[name] = column; + }); + + // When none of the columns have a value, it's ambiguous whether the user + // intends to replace a row of default values or append a SELECT statement + // later. Resolve the ambiguity by assuming that if no columns are specified + // it is a row of default values, otherwise a SELECT will be added. + if (hasValues || !hasColumns) { + this.valueSets.push(values); + } + + return self; +}; + +/* + * Get parameters for all values to be replaced. This function + * handles handles bulk replaces, where keys may be present + * in some objects and not others. When keys are not present, + * the replace should refer to the column value as DEFAULT. + */ +Replace.prototype.getParameters = function () { + var self = this; + return this.valueSets + .map(function (nodeDict) { + var set = []; + self.names.forEach(function (name) { + var node = nodeDict[name]; + if (node) { + set.push(ParameterNode.getNodeOrParameterNode(node.value)); + } + else { + set.push(new DefaultNode()); + } + }); + return set; + }); +}; diff --git a/lib/node/restrict.js b/lib/node/restrict.js index f7c63c15..942f4112 100644 --- a/lib/node/restrict.js +++ b/lib/node/restrict.js @@ -1,6 +1,6 @@ 'use strict'; -var Node = require(__dirname); +var Node = require('./index'); module.exports = Node.define({ type: 'RESTRICT' diff --git a/lib/node/returning.js b/lib/node/returning.js index 35ea0a18..2f27d067 100644 --- a/lib/node/returning.js +++ b/lib/node/returning.js @@ -1,6 +1,6 @@ 'use strict'; -var Node = require(__dirname); +var Node = require('./index'); module.exports = Node.define({ type: 'RETURNING' diff --git a/lib/node/select.js b/lib/node/select.js index ce5b5768..93e22538 100644 --- a/lib/node/select.js +++ b/lib/node/select.js @@ -1,6 +1,6 @@ 'use strict'; -var Node = require(__dirname); +var Node = require('./index'); module.exports = Node.define({ type: 'SELECT', @@ -10,6 +10,8 @@ module.exports = Node.define({ this.sql = arg.sql; } // used when processing LIMIT clauses in MSSQL - this.msSQLLimitNode=undefined; + 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 index ab32f314..86c1ce21 100644 --- a/lib/node/slice.js +++ b/lib/node/slice.js @@ -1,8 +1,8 @@ 'use strict'; var _ = require('lodash'); -var Node = require(__dirname); -var valueExpressionMixin = require(__dirname + '/valueExpression'); +var Node = require('./index'); +var valueExpressionMixin = require('./valueExpression'); var valueExpressionMixed = false; var SliceNode = Node.define({ @@ -22,7 +22,7 @@ var SliceNode = Node.define({ }); // allow aliasing -var AliasNode = require(__dirname + '/alias'); +var AliasNode = require('./alias'); _.extend(SliceNode.prototype, AliasNode.AliasMixin); module.exports = SliceNode; diff --git a/lib/node/table.js b/lib/node/table.js index b1257105..4fca70d1 100644 --- a/lib/node/table.js +++ b/lib/node/table.js @@ -1,6 +1,6 @@ 'use strict'; -var Node = require(__dirname); +var Node = require('./index'); module.exports = Node.define({ type: 'TABLE', constructor: function(table) { diff --git a/lib/node/ternary.js b/lib/node/ternary.js index 1ef5ce7b..3f688ba9 100644 --- a/lib/node/ternary.js +++ b/lib/node/ternary.js @@ -1,8 +1,8 @@ 'use strict'; var _ = require('lodash'); -var Node = require(__dirname); -var valueExpressionMixin = require(__dirname + '/valueExpression'); +var Node = require('./index'); +var valueExpressionMixin = require('./valueExpression'); var valueExpressionMixed = false; var TernaryNode = Node.define(_.extend({ @@ -25,7 +25,7 @@ var TernaryNode = Node.define(_.extend({ })); // allow aliasing -var AliasNode = require(__dirname + '/alias'); +var AliasNode = require('./alias'); _.extend(TernaryNode.prototype, AliasNode.AliasMixin); module.exports = TernaryNode; diff --git a/lib/node/text.js b/lib/node/text.js index ef1194b2..bf026151 100644 --- a/lib/node/text.js +++ b/lib/node/text.js @@ -1,6 +1,6 @@ 'use strict'; -var Node = require(__dirname); +var Node = require('./index'); module.exports = Node.define({ type: 'TEXT', diff --git a/lib/node/truncate.js b/lib/node/truncate.js new file mode 100644 index 00000000..165e0d9e --- /dev/null +++ b/lib/node/truncate.js @@ -0,0 +1,12 @@ +'use strict'; + +var Node = require('./index'); + +module.exports = Node.define({ + type: 'TRUNCATE', + + constructor: function(table) { + Node.call(this); + this.add(table); + } +}); diff --git a/lib/node/update.js b/lib/node/update.js index 32a29ba9..890fbee9 100644 --- a/lib/node/update.js +++ b/lib/node/update.js @@ -1,6 +1,6 @@ 'use strict'; -var Node = require(__dirname); +var Node = require('./index'); module.exports = Node.define({ type: 'UPDATE' diff --git a/lib/node/valueExpression.js b/lib/node/valueExpression.js index 876872cd..2fa741a9 100644 --- a/lib/node/valueExpression.js +++ b/lib/node/valueExpression.js @@ -77,18 +77,6 @@ var ValueExpressionMixin = function() { return new SliceNode(this.toNode(), processParams(start), processParams(end)); }; - var containsMethod = function(set) { - return new ContainsNode(this.toNode(), processParams(set)); - }; - - var containedByMethod = function(set) { - return new ContainedByNode(this.toNode(), processParams(set)); - }; - - var overlapMethod = function(set) { - return new OverlapNode(this.toNode(), processParams(set)); - }; - var castMethod = function(dataType) { return new CastNode(this.toNode(), dataType); }; @@ -103,15 +91,15 @@ var ValueExpressionMixin = function() { }; var caseMethod = function(whenList, thenList, elseBranch) { - if (undefined != elseBranch) { + if (undefined !== elseBranch) { elseBranch = processParams(elseBranch); } return new CaseNode({ whenList : processParams(whenList), thenList : processParams(thenList), else : elseBranch - }) - } + }); + }; return { isNull : postfixUnaryMethod('IS NULL'), @@ -138,8 +126,10 @@ var ValueExpressionMixin = function() { bitwiseOr : binaryMethod('|'), bitwiseXor : binaryMethod('#'), regex : binaryMethod('~'), + iregex : binaryMethod('~*'), regexp : binaryMethod('REGEXP'), notRegex : binaryMethod('!~'), + notIregex : binaryMethod('!~*'), concat : binaryMethod('||'), key : binaryMethod('->'), keyText : binaryMethod('->>'), @@ -154,9 +144,11 @@ var ValueExpressionMixin = function() { 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, diff --git a/lib/node/where.js b/lib/node/where.js index b542807d..5c0465fc 100644 --- a/lib/node/where.js +++ b/lib/node/where.js @@ -1,8 +1,8 @@ 'use strict'; -var Node = require(__dirname); -var BinaryNode = require(__dirname + '/binary'); -var TextNode = require(__dirname + '/text'); +var Node = require('./index'); +var BinaryNode = require('./binary'); +var TextNode = require('./text'); var normalizeNode = function(table, node) { var result = node; diff --git a/lib/table.js b/lib/table.js index 5763ecf7..1408542b 100644 --- a/lib/table.js +++ b/lib/table.js @@ -3,20 +3,23 @@ var util = require('util'); var lodash = require('lodash'); -var Query = require(__dirname + '/node/query'); -var Column = require(__dirname + '/column'); -var TableNode = require(__dirname + '/node/table'); -var JoinNode = require(__dirname + '/node/join'); -var LiteralNode = require(__dirname + '/node/literal'); -var Joiner = require(__dirname + '/joiner'); +var Query = require('./node/query'); +var Column = require('./column'); +var TableNode = require('./node/table'); +var JoinNode = require('./node/join'); +var LiteralNode = require('./node/literal'); +var Joiner = require('./joiner'); +var ForeignKeyNode = require('./node/foreignKey'); var Table = function(config) { this._schema = config.schema; this._name = config.name; this._initialConfig = config; this.columnWhiteList = !!config.columnWhiteList; + this.isTemporary=!!config.isTemporary; this.snakeToCamel = !!config.snakeToCamel; this.columns = []; + this.foreignKeys = []; this.table = this; if (!config.sql) { config.sql = require('./index'); @@ -44,6 +47,16 @@ Table.define = function(config) { for (var i = 0; i < config.columns.length; i++) { table.addColumn(config.columns[i]); } + + if(config.foreignKeys !== undefined) { + if(util.isArray(config.foreignKeys)) { + for(i = 0; i < config.foreignKeys.length; i++) { + table.foreignKeys.push(new ForeignKeyNode(config.foreignKeys[i])); + } + } else { + table.foreignKeys.push(new ForeignKeyNode(config.foreignKeys)); + } + } return table; }; @@ -54,7 +67,8 @@ Table.prototype.clone = function(config) { sql: this.sql, columnWhiteList: !!this.columnWhiteList, snakeToCamel: !!this.snakeToCamel, - columns: this.columns + columns: this.columns, + foreignKeys: this.foreignKeys }, config || {})); }; @@ -66,6 +80,20 @@ Table.prototype.createColumn = function(col) { col.table = this; col = new Column(col); + + // Load subfields from array into an object of form name: Column + if(util.isArray(col.subfields)) { + col.subfields = lodash.chain(col.subfields) + .map(lodash.bind(function (subfield) { + return [subfield, new Column({ + table: this, + subfieldContainer: col, + name: subfield + })]; + }, this)) + .fromPairs() + .value(); + } } return col; @@ -83,7 +111,7 @@ Table.prototype.addColumn = function(col, options) { } else { return this; } - } else if(!!this[col.name] && (process.env.NODE_ENV !== 'test')) { + } else if(!!this[col.name] && (process.env.NODE_ENV === 'debug')) { console.log('Please notice that you have just defined the column "' + col.name + '". In order to access it, you need to use "table.getColumn(\'' + col.name + '\');"!'); } this.columns.push(col); @@ -98,13 +126,10 @@ Table.prototype.addColumn = function(col, options) { }; Table.prototype.hasColumn = function(col) { - col = this.createColumn(col); - - var cols = this.columns.filter(function(column) { - return column.property === col.property || column.name === col.name; + var columnName = col instanceof Column ? col.name : col; + return this.columns.some(function(column) { + return column.property === columnName || column.name === columnName; }); - - return cols.length > 0; }; Table.prototype.getColumn = @@ -130,6 +155,7 @@ Table.prototype.setSchema = function(schema) { }; Table.prototype.getName = function() { + if (this.sql && this.sql.dialectName=="mssql" && this.isTemporary) return "#"+this._name; return this._name; }; @@ -166,12 +192,6 @@ Table.prototype.select = function() { return query; }; -Table.prototype.from = function() { - var query = new Query(this); - query.from.apply(query, arguments); - return query; -}; - Table.prototype.subQuery = function(alias) { // create the query and pass it off var query = new Query(this); @@ -185,7 +205,7 @@ Table.prototype.subQuery = function(alias) { Table.prototype.insert = function() { var query = new Query(this); - if(arguments[0].length == 0){ + if(!arguments[0] || (util.isArray(arguments[0]) && arguments[0].length === 0)){ query.select.call(query, this.star()); query.where.apply(query,["1=2"]); } else { @@ -194,33 +214,14 @@ Table.prototype.insert = function() { return query; }; -Table.prototype.update = function() { - var query = new Query(this); - query.update.apply(query, arguments); - return query; -}; - -Table.prototype['delete'] = function() { - var query = new Query(this); - query['delete'].apply(query, arguments); - return query; -}; - -Table.prototype.create = function() { +Table.prototype.replace = function() { var query = new Query(this); - query.create.apply(query, arguments); - return query; -}; - -Table.prototype.drop = function() { - var query = new Query(this); - query.drop.apply(query, arguments); - return query; -}; - -Table.prototype.alter = function() { - var query = new Query(this); - query.alter.apply(query, arguments); + if(!arguments[0] || (util.isArray(arguments[0]) && arguments[0].length === 0)){ + query.select.call(query, this.star()); + query.where.apply(query,["1=2"]); + } else { + query.replace.apply(query, arguments); + } return query; }; @@ -236,6 +237,10 @@ Table.prototype.leftJoin = function(other) { return new JoinNode('LEFT', this.toNode(), other.toNode()); }; +Table.prototype.leftJoinLateral = function(other) { + return new JoinNode('LEFT LATERAL', this.toNode(), other.toNode()); +}; + // auto-join tables based on column intropsection Table.prototype.joinTo = function(other) { return Joiner.leftJoin(this, other); @@ -253,26 +258,37 @@ Table.prototype.__defineGetter__("nodes", function() { return this.select(this.star()).nodes; }); -Table.prototype.where = function() { - var query = new Query(this); - query.where.apply(query, arguments); - return query; -}; - Table.prototype.and = function() { var query = new Query(this); query.where.apply(query, arguments); return query; }; -Table.prototype.or = function() { - var query = new Query(this); - query.or.apply(query, arguments); - return query; -}; - Table.prototype.indexes = function() { return new Query(this).indexes(); }; +var queryMethods = [ + 'alter', + 'create', + 'delete', + 'drop', + 'from', + 'limit', + 'offset', + 'or', + 'order', + 'truncate', + 'update', + 'where' +]; + +queryMethods.forEach(function (method) { + Table.prototype[method] = function () { + var query = new Query(this); + query[method].apply(query, arguments); + return query; + }; +}); + module.exports = Table; diff --git a/lib/types.d.ts b/lib/types.d.ts new file mode 100644 index 00000000..a7011f99 --- /dev/null +++ b/lib/types.d.ts @@ -0,0 +1,221 @@ + +/** + * This is an adaptation of https://github.com/doxout/anydb-sql/blob/4e4c0ff4a7f2efb7f820baaafea1f624f1ae0399/d.ts/anydb-sql.d.ts + * Whole project is MIT licensed, so, we can use it. We also feed back any + * improvements, questions, concerns. + */ +declare module "sql" { + + type SQLDialects = + | "mssql" + | "mysql" + | "oracle" + | "postgres" + | "sqlite" + ; + + interface OrderByValueNode {} + + interface Named { + name?: Name; + } + interface ColumnDefinition extends Named { + jsType?: Type; + dataType: string; + primaryKey?: boolean; + references?: { + table:string; + column: string; + onDelete?: 'restrict' | 'cascade' | 'no action' | 'set null' | 'set default'; + onUpdate?: 'restrict' | 'cascade' | 'no action' | 'set null' | 'set default'; + }; + notNull?: boolean; + unique?: boolean; + defaultValue?: Type; + } + + interface TableDefinition { + name: Name; + schema: string; + columns: {[CName in ((keyof Row) & string)]: ColumnDefinition}; + dialect?: SQLDialects; + isTemporary?: boolean; + foreignKeys?: { + table: string, + columns: (keyof Row)[], + refColumns: string[], + onDelete?: 'restrict' | 'cascade' | 'no action' | 'set null' | 'set default'; + onUpdate?: 'restrict' | 'cascade' | 'no action' | 'set null' | 'set default'; + } + } + + interface QueryLike { + values: any[] + text:string + } + + interface Executable { + toQuery():QueryLike; + } + + interface Queryable extends Executable { + where(...nodes:any[]):Query + delete():ModifyingQuery + select(star: Column): Query; + select(n1: Column):Query<{[N in N1]: T1}>; + select( + n1: Column, + n2: Column):Query<{[N in N1]: T1} & {[N in N2]: T2}> + select( + n1: Column, + n2: Column, + n3: Column):Query<{[N in N1]: T1} & {[N in N2]: T2} & {[N in N3]: T3}> + select(...nodesOrTables:any[]):Query + + } + + interface Query extends Executable, Queryable { + resultType: T; + + from(table:TableNode):Query + from(statement:string):Query + update(o:{[key: string]:any}):ModifyingQuery + update(o:{}):ModifyingQuery + group(...nodes:any[]):Query + order(...criteria:OrderByValueNode[]):Query + limit(l:number):Query + offset(o:number):Query + } + + interface SubQuery { + select(node:Column):SubQuery + select(...nodes: any[]):SubQuery + where(...nodes:any[]):SubQuery + from(table:TableNode):SubQuery + from(statement:string):SubQuery + group(...nodes:any[]):SubQuery + order(criteria:OrderByValueNode):SubQuery + exists():BinaryNode + notExists(): BinaryNode; + notExists(subQuery:SubQuery):BinaryNode + } + + + interface ModifyingQuery extends Executable { + returning(...nodes:any[]):Query + where(...nodes:any[]):ModifyingQuery + } + + interface TableNode { + join(table:TableNode):JoinTableNode + leftJoin(table:TableNode):JoinTableNode + } + + interface JoinTableNode extends TableNode { + on(filter:BinaryNode):TableNode + on(filter:string):TableNode + } + + interface CreateQuery extends Executable { + ifNotExists():Executable + } + interface DropQuery extends Executable { + ifExists():Executable + } + + type Columns = { + [Name in keyof T]: Column + } + type Table = TableNode & Queryable & Named & Columns & { + getName(): string; + getSchema(): string; + + literal(statement: string): any; + + create():CreateQuery + drop():DropQuery + as(name:OtherName):Table + update(o: Partial):ModifyingQuery + insert(row:T):ModifyingQuery + insert(rows:T[]):ModifyingQuery + select():Query + select(...nodes:any[]):Query + from(table:TableNode):Query + from(statement:string):Query + star():Column + subQuery():SubQuery + columns:Column[] + sql: SQL; + alter():AlterQuery; + indexes(): IndexQuery; + } + + interface AlterQuery extends Executable { + addColumn(column:Column): AlterQuery; + addColumn(name: string, options:string): AlterQuery; + dropColumn(column: Column|string): AlterQuery; + renameColumn(column: Column, newColumn: Column):AlterQuery; + renameColumn(column: Column, newName: string):AlterQuery; + renameColumn(name: string, newName: string):AlterQuery; + rename(newName: string): AlterQuery + } + interface IndexQuery { + create(): IndexCreationQuery; + create(indexName: string): IndexCreationQuery; + drop(indexName: string): Executable; + drop(...columns: Column[]): Executable + } + interface IndexCreationQuery extends Executable { + unique(): IndexCreationQuery; + using(name: string): IndexCreationQuery; + on(...columns: (Column|OrderByValueNode)[]): IndexCreationQuery; + withParser(parserName: string): IndexCreationQuery; + fulltext(): IndexCreationQuery; + spatial(): IndexCreationQuery; + } + + interface SQL { + functions: { + LOWER(c:Column):Column + } + } + + interface BinaryNode { + and(node:BinaryNode):BinaryNode + or(node:BinaryNode):BinaryNode + } + + interface Column { + name: Name + in(arr:T[]):BinaryNode + in(subQuery:SubQuery):BinaryNode + notIn(arr:T[]):BinaryNode + equals(node: T|Column):BinaryNode + notEquals(node: T|Column):BinaryNode + gte(node: T|Column):BinaryNode + lte(node: T|Column):BinaryNode + gt(node:T|Column):BinaryNode + lt(node: T|Column):BinaryNode + like(str:string):BinaryNode + multiply:{ + (node:Column):Column + (n:number):Column //todo check column names + } + isNull():BinaryNode + isNotNull():BinaryNode + //todo check column names + sum():Column + count():Column + count(name:string):Column + distinct():Column + as(name:OtherName):Column + ascending:OrderByValueNode + descending:OrderByValueNode + asc:OrderByValueNode + desc:OrderByValueNode + } + + function define(map:TableDefinition): Table; + function setDialect(dialect: SQLDialects): void; + +} diff --git a/package.json b/package.json index d7bcc7d7..dc250c01 100644 --- a/package.json +++ b/package.json @@ -2,22 +2,26 @@ "author": "brianc ", "name": "sql", "description": "sql builder", - "version": "0.47.0", + "version": "0.78.0", "homepage": "https://github.com/brianc/node-sql", + "license": "MIT", "repository": { "type": "git", "url": "git://github.com/brianc/node-sql.git" }, "main": "lib/", + "types": "lib/types.d.ts", "scripts": { - "test": "node ./runtests" + "test": "node_modules/.bin/mocha", + "lint": "jshint lib test", + "posttest": "jshint lib test" }, "engines": { "node": "*" }, "dependencies": { "sliced": "0.0.x", - "lodash": "1.3.x" + "lodash": "4.1.x" }, "devDependencies": { "jshint": "*", diff --git a/runtests.js b/runtests.js index 8b722efe..ac89769b 100644 --- a/runtests.js +++ b/runtests.js @@ -1,18 +1,18 @@ -var childProcess = require("child_process") -var path = require("path") +var childProcess = require("child_process"); +var path = require("path"); -var env = process.env -env.NODE_ENV = "test" +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) +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) -}) + process.exit(code); +}); diff --git a/test/binary-clause-tests.js b/test/binary-clause-tests.js index f4affdc6..10c50737 100644 --- a/test/binary-clause-tests.js +++ b/test/binary-clause-tests.js @@ -33,7 +33,9 @@ test('operators', function() { 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'); diff --git a/test/column-tests.js b/test/column-tests.js index 9bcc15ac..d0b95a14 100644 --- a/test/column-tests.js +++ b/test/column-tests.js @@ -6,7 +6,7 @@ var sql = require(__dirname + '/../lib'); describe('column', function() { var table = sql.define({ name: 'user', - columns: ['id', 'created'] + columns: ['id', 'created', 'alias'] }); it('can be accessed by property and array', function() { @@ -18,6 +18,10 @@ describe('column', function() { assert.equal(table.id.toQuery().text, '"user"."id"'); }); + it('works with a column name of "alias"', function() { + assert.equal(table.alias.toQuery().text, '"user"."alias"'); + }); + it('respects AS rename', function() { assert.equal(table.id.as('userId').toQuery().text, '"user"."id" AS "userId"'); }); @@ -32,29 +36,29 @@ describe('column', function() { 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('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({ @@ -92,10 +96,10 @@ describe('column', function() { 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); + 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); + assert.throws(function() { table.update({id:0, _private:'_private', name:'name'}); }, Error); }); }); diff --git a/test/dialects/aggregate-tests.js b/test/dialects/aggregate-tests.js index a58397cc..59d48d57 100644 --- a/test/dialects/aggregate-tests.js +++ b/test/dialects/aggregate-tests.js @@ -16,13 +16,17 @@ Harness.test({ string: 'SELECT COUNT("post".*) AS "post_count" FROM "post"' }, mysql: { - text : 'SELECT COUNT(`post`.*) AS `post_count` FROM `post`', - string: 'SELECT COUNT(`post`.*) AS `post_count` FROM `post`' + text : 'SELECT COUNT(*) AS `post_count` FROM `post`', + string: 'SELECT COUNT(*) AS `post_count` FROM `post`' }, mssql: { text : 'SELECT COUNT(*) AS [post_count] FROM [post]', string: 'SELECT COUNT(*) AS [post_count] FROM [post]' }, + oracle: { + text : 'SELECT COUNT(*) "post_count" FROM "post"', + string: 'SELECT COUNT(*) "post_count" FROM "post"' + }, params: [] }); @@ -37,13 +41,17 @@ Harness.test({ string: 'SELECT COUNT("post".*) AS "post_count" FROM "post"' }, msyql: { - text : 'SELECT COUNT(`post`.*) AS `post_count` FROM `post`', - string: 'SELECT COUNT(`post`.*) AS `post_count` FROM `post`' + text : 'SELECT COUNT(*) AS `post_count` FROM `post`', + string: 'SELECT COUNT(*) AS `post_count` FROM `post`' }, mssql: { text : 'SELECT COUNT(*) AS [post_count] FROM [post]', string: 'SELECT COUNT(*) AS [post_count] FROM [post]' }, + oracle: { + text : 'SELECT COUNT(*) "post_count" FROM "post"', + string: 'SELECT COUNT(*) "post_count" FROM "post"' + }, params: [] }); @@ -58,13 +66,17 @@ Harness.test({ string: 'SELECT COUNT("post".*) AS "post_amount" FROM "post"' }, mysql: { - text : 'SELECT COUNT(`post`.*) AS `post_amount` FROM `post`', - string: 'SELECT COUNT(`post`.*) AS `post_amount` FROM `post`' + text : 'SELECT COUNT(*) AS `post_amount` FROM `post`', + string: 'SELECT COUNT(*) AS `post_amount` FROM `post`' }, mssql: { text : 'SELECT COUNT(*) AS [post_amount] FROM [post]', string: 'SELECT COUNT(*) AS [post_amount] FROM [post]' }, + oracle: { + text : 'SELECT COUNT(*) "post_amount" FROM "post"', + string: 'SELECT COUNT(*) "post_amount" FROM "post"' + }, params: [] }); @@ -86,6 +98,10 @@ Harness.test({ 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: [] }); @@ -107,6 +123,10 @@ Harness.test({ 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: [] }); @@ -128,6 +148,10 @@ Harness.test({ 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: [] }); @@ -142,8 +166,12 @@ Harness.test({ string: 'SELECT COUNT("customer".*) AS "customer_count" FROM "customer"' }, mysql: { - text : 'SELECT COUNT(`customer`.*) AS `customer_count` FROM `customer`', - string: 'SELECT COUNT(`customer`.*) AS `customer_count` FROM `customer`' + 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: [] }); @@ -166,6 +194,10 @@ Harness.test({ 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: [] }); @@ -187,6 +219,10 @@ Harness.test({ 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: [] }); @@ -208,6 +244,10 @@ Harness.test({ 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: [] }); @@ -229,6 +269,10 @@ Harness.test({ 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: [] }); @@ -250,6 +294,10 @@ Harness.test({ 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: [] }); @@ -271,6 +319,10 @@ Harness.test({ 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: [] }); @@ -292,6 +344,10 @@ Harness.test({ 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: [] }); @@ -313,6 +369,10 @@ Harness.test({ 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: [] }); @@ -334,6 +394,10 @@ Harness.test({ 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: [] }); @@ -355,6 +419,10 @@ Harness.test({ 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: [] }); @@ -376,6 +444,10 @@ Harness.test({ 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: [] }); @@ -397,5 +469,9 @@ Harness.test({ text : 'SELECT AVG([post].[id]) AS [avg_id] FROM [post]', string: 'SELECT AVG([post].[id]) AS [avg_id] FROM [post]' }, + oracle: { + text : 'SELECT AVG("post"."id") "avg_id" FROM "post"', + string: 'SELECT AVG("post"."id") "avg_id" FROM "post"' + }, params: [] }); diff --git a/test/dialects/alias-tests.js b/test/dialects/alias-tests.js index a6a5a09f..32db9308 100644 --- a/test/dialects/alias-tests.js +++ b/test/dialects/alias-tests.js @@ -2,6 +2,7 @@ var Harness = require('./support'); var customer = Harness.defineCustomerTable(); +var Sql = require('../../lib').setDialect('postgres'); Harness.test({ query: customer.select(customer.name.isNull().as('nameIsNull')), @@ -21,6 +22,10 @@ Harness.test({ 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: [] }); @@ -42,6 +47,10 @@ Harness.test({ 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] }); @@ -63,5 +72,51 @@ Harness.test({ text : 'SELECT ([customer].[age] BETWEEN @1 AND @2) AS [ageBetween] FROM [customer]', string: 'SELECT ([customer].[age] BETWEEN 10 AND 20) AS [ageBetween] FROM [customer]' }, + oracle: { + text : 'SELECT ("customer"."age" BETWEEN :1 AND :2) "ageBetween" FROM "customer"', + string: 'SELECT ("customer"."age" BETWEEN 10 AND 20) "ageBetween" FROM "customer"' + }, + params: [10, 20] +}); + +Harness.test({ + query: customer.select(Sql.functions.ROUND(customer.age.as('ageBetween'), 2)), + pg: { + text : 'SELECT ROUND("customer"."age", $1) FROM "customer"', + string: 'SELECT ROUND("customer"."age", 2) FROM "customer"' + }, + sqlite: { + text : 'SELECT ROUND("customer"."age", $1) FROM "customer"', + string: 'SELECT ROUND("customer"."age", 2) FROM "customer"' + }, + mysql: { + text : 'SELECT ROUND(`customer`.`age`, ?) FROM `customer`', + string: 'SELECT ROUND(`customer`.`age`, 2) FROM `customer`' + }, + mssql: { + text : 'SELECT ROUND([customer].[age], @1) FROM [customer]', + string: 'SELECT ROUND([customer].[age], 2) FROM [customer]' + }, + oracle: { + text : 'SELECT ROUND("customer"."age", :1) FROM "customer"', + string: 'SELECT ROUND("customer"."age", 2) FROM "customer"' + }, + params: [2] +}); + +Harness.test({ + query: customer.select(customer.age.notBetween(10, 20).as('ageNotBetween')), + pg: { + text : 'SELECT ("customer"."age" NOT BETWEEN $1 AND $2) AS "ageNotBetween" FROM "customer"', + string: 'SELECT ("customer"."age" NOT BETWEEN 10 AND 20) AS "ageNotBetween" FROM "customer"' + }, + sqlite: { + text : 'SELECT ("customer"."age" NOT BETWEEN $1 AND $2) AS "ageNotBetween" FROM "customer"', + string: 'SELECT ("customer"."age" NOT BETWEEN 10 AND 20) AS "ageNotBetween" FROM "customer"' + }, + mysql: { + text : 'SELECT (`customer`.`age` NOT BETWEEN ? AND ?) AS `ageNotBetween` FROM `customer`', + string: 'SELECT (`customer`.`age` NOT BETWEEN 10 AND 20) AS `ageNotBetween` FROM `customer`' + }, params: [10, 20] }); diff --git a/test/dialects/alter-table-tests.js b/test/dialects/alter-table-tests.js index d9e83d94..d050da3d 100644 --- a/test/dialects/alter-table-tests.js +++ b/test/dialects/alter-table-tests.js @@ -22,6 +22,10 @@ Harness.test({ 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: [] }); @@ -43,6 +47,10 @@ Harness.test({ 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: [] }); @@ -64,6 +72,10 @@ Harness.test({ 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: [] }); @@ -118,6 +130,10 @@ Harness.test({ 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: [] }); @@ -139,6 +155,10 @@ Harness.test({ 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: [] }); @@ -160,6 +180,10 @@ Harness.test({ 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: [] }); @@ -178,10 +202,8 @@ Harness.test({ throws: true }, mssql: { - text : 'Mssql renaming columns not yet implemented', - throws: true -// text : 'EXEC sp_rename [group.userId], [newUserId], \'COLUMN\'', -// string: 'EXEC sp_rename [group.userId], [newUserId], \'COLUMN\'' + text : 'EXEC sp_rename \'[group].[userId]\', [newUserId], \'COLUMN\'', + string: 'EXEC sp_rename \'[group].[userId]\', [newUserId], \'COLUMN\'' }, params: [] }); @@ -201,10 +223,8 @@ Harness.test({ string: 'ALTER TABLE `group` CHANGE COLUMN `userId` `newUserId` varchar(100)' }, mssql: { - text : 'Mssql renaming columns not yet implemented', - throws: true -// text : 'EXEC sp_rename [group.userId], [newUserId], \'COLUMN\'', -// string: 'EXEC sp_rename [group.userId], [newUserId], \'COLUMN\'' + text : 'EXEC sp_rename \'[group].[userId]\', [newUserId], \'COLUMN\'', + string: 'EXEC sp_rename \'[group].[userId]\', [newUserId], \'COLUMN\'' }, params: [] }); @@ -224,10 +244,8 @@ Harness.test({ string: 'ALTER TABLE `group` CHANGE COLUMN `userId` `id` varchar(100)' }, mssql: { - text : 'Mssql renaming columns not yet implemented', - throws: true -// text : 'EXEC sp_rename [group.userId], [id], \'COLUMN\'', -// string: 'EXEC sp_rename [group.userId], [id], \'COLUMN\'' + text : 'EXEC sp_rename \'[group].[userId]\', [id], \'COLUMN\'', + string: 'EXEC sp_rename \'[group].[userId]\', [id], \'COLUMN\'' }, params: [] }); @@ -256,10 +274,8 @@ Harness.test({ throws: true }, mssql: { - text : 'Mssql renaming columns not yet implemented', - throws: true -// text : 'EXEC sp_rename [UserWithSignature.Signature], [sig], \'COLUMN\'', -// string: 'EXEC sp_rename [UserWithSignature.Signature], [sig], \'COLUMN\'' + text : 'EXEC sp_rename \'[UserWithSignature].[Signature]\', [sig], \'COLUMN\'', + string: 'EXEC sp_rename \'[UserWithSignature].[Signature]\', [sig], \'COLUMN\'' } }); @@ -282,16 +298,20 @@ var post = Table.define({ 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)' + 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)' + 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)' + 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: [] }); @@ -310,5 +330,9 @@ Harness.test({ 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/binary-clause-tests.js b/test/dialects/binary-clause-tests.js index 28b4a155..69c355fe 100644 --- a/test/dialects/binary-clause-tests.js +++ b/test/dialects/binary-clause-tests.js @@ -2,8 +2,10 @@ 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: { @@ -22,6 +24,31 @@ Harness.test({ 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: [] }); @@ -43,6 +70,10 @@ Harness.test({ 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: ['!'] }); @@ -64,5 +95,9 @@ Harness.test({ 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 index f07bb0a7..f492a4f5 100644 --- a/test/dialects/case-tests.js +++ b/test/dialects/case-tests.js @@ -12,7 +12,7 @@ Harness.test({ }, sqlite: { 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"' + 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`', @@ -23,6 +23,11 @@ Harness.test({ 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] }); @@ -35,7 +40,7 @@ Harness.test({ }, 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 TRUE THEN 0 WHEN FALSE THEN 1 ELSE 2 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`', @@ -46,6 +51,11 @@ Harness.test({ 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] }); @@ -58,7 +68,7 @@ Harness.test({ }, sqlite: { 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"' + 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`', @@ -69,6 +79,11 @@ Harness.test({ 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] }); @@ -81,7 +96,7 @@ Harness.test({ }, 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 TRUE THEN 0 WHEN FALSE THEN 1 ELSE ("customer"."age" BETWEEN 10 AND 20) 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`', @@ -92,6 +107,11 @@ Harness.test({ 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] }); @@ -104,7 +124,7 @@ Harness.test({ }, sqlite: { 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"' + 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`', @@ -115,6 +135,11 @@ Harness.test({ 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] }); @@ -137,6 +162,10 @@ Harness.test({ 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] }); @@ -159,6 +188,10 @@ Harness.test({ 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] }); @@ -181,5 +214,9 @@ Harness.test({ 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 index 4bf14c22..2295d720 100644 --- a/test/dialects/cast-tests.js +++ b/test/dialects/cast-tests.js @@ -2,6 +2,7 @@ var Harness = require('./support'); var customer = Harness.defineCustomerTable(); +var customerAlias = Harness.defineCustomerAliasTable(); // Cast columns. Harness.test({ @@ -22,6 +23,10 @@ Harness.test({ 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: [] }); @@ -43,6 +48,10 @@ Harness.test({ 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: [] }); @@ -65,6 +74,10 @@ Harness.test({ 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: [] }); @@ -87,6 +100,10 @@ Harness.test({ 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: [] }); @@ -109,6 +126,10 @@ Harness.test({ 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] }); @@ -131,5 +152,163 @@ Harness.test({ text : 'SELECT CAST([customer].[age] AS int) AS [age_int] FROM [customer]', string: 'SELECT CAST([customer].[age] AS int) AS [age_int] FROM [customer]' }, + oracle: { + text : 'SELECT CAST("customer"."age" AS int) "age_int" FROM "customer"', + string: 'SELECT CAST("customer"."age" AS int) "age_int" FROM "customer"' + }, + params: [] +}); + +// Cast Aliased columns. +Harness.test({ + query: customerAlias.select(customerAlias.age_alias.cast('int')), + pg: { + text : 'SELECT CAST("customer"."age" AS int) FROM "customer"', + string: 'SELECT CAST("customer"."age" AS int) FROM "customer"' + }, + sqlite: { + text : 'SELECT CAST("customer"."age" AS int) FROM "customer"', + string: 'SELECT CAST("customer"."age" AS int) FROM "customer"' + }, + mysql: { + text : 'SELECT CAST(`customer`.`age` AS int) FROM `customer`', + string: 'SELECT CAST(`customer`.`age` AS int) FROM `customer`' + }, + mssql: { + text : 'SELECT CAST([customer].[age] AS int) FROM [customer]', + string: 'SELECT CAST([customer].[age] AS int) FROM [customer]' + }, + oracle: { + text : 'SELECT CAST("customer"."age" AS int) FROM "customer"', + string: 'SELECT CAST("customer"."age" AS int) FROM "customer"' + }, + params: [] +}); + +Harness.test({ + query: customerAlias.select(customerAlias.name_alias.cast('varchar(10)')), + pg: { + text : 'SELECT CAST("customer"."name" AS varchar(10)) FROM "customer"', + string: 'SELECT CAST("customer"."name" AS varchar(10)) FROM "customer"' + }, + sqlite: { + text : 'SELECT CAST("customer"."name" AS varchar(10)) FROM "customer"', + string: 'SELECT CAST("customer"."name" AS varchar(10)) FROM "customer"' + }, + mysql: { + text : 'SELECT CAST(`customer`.`name` AS varchar(10)) FROM `customer`', + string: 'SELECT CAST(`customer`.`name` AS varchar(10)) FROM `customer`' + }, + mssql: { + text : 'SELECT CAST([customer].[name] AS varchar(10)) FROM [customer]', + string: 'SELECT CAST([customer].[name] AS varchar(10)) FROM [customer]' + }, + oracle: { + text : 'SELECT CAST("customer"."name" AS varchar(10)) FROM "customer"', + string: 'SELECT CAST("customer"."name" AS varchar(10)) FROM "customer"' + }, + params: [] +}); +// Cast binary expressions for Aliased Column. +Harness.test({ + query: customerAlias.select(customerAlias.name_alias.plus(customerAlias.age_alias).cast('varchar(15)')), + pg: { + text : 'SELECT CAST(("customer"."name" + "customer"."age") AS varchar(15)) FROM "customer"', + string: 'SELECT CAST(("customer"."name" + "customer"."age") AS varchar(15)) FROM "customer"' + }, + sqlite: { + text : 'SELECT CAST(("customer"."name" + "customer"."age") AS varchar(15)) FROM "customer"', + string: 'SELECT CAST(("customer"."name" + "customer"."age") AS varchar(15)) FROM "customer"' + }, + mysql: { + text : 'SELECT CAST((`customer`.`name` + `customer`.`age`) AS varchar(15)) FROM `customer`', + string: 'SELECT CAST((`customer`.`name` + `customer`.`age`) AS varchar(15)) FROM `customer`' + }, + mssql: { + text : 'SELECT CAST(([customer].[name] + [customer].[age]) AS varchar(15)) FROM [customer]', + string: 'SELECT CAST(([customer].[name] + [customer].[age]) AS varchar(15)) FROM [customer]' + }, + oracle: { + text : 'SELECT CAST(("customer"."name" + "customer"."age") AS varchar(15)) FROM "customer"', + string: 'SELECT CAST(("customer"."name" + "customer"."age") AS varchar(15)) FROM "customer"' + }, + params: [] +}); + +// Cast cast expressions for aliased columns. +Harness.test({ + query: customerAlias.select(customerAlias.name_alias.cast('varchar(15)').cast('varchar(10)')), + pg: { + text : 'SELECT CAST(CAST("customer"."name" AS varchar(15)) AS varchar(10)) FROM "customer"', + string: 'SELECT CAST(CAST("customer"."name" AS varchar(15)) AS varchar(10)) FROM "customer"' + }, + sqlite: { + text : 'SELECT CAST(CAST("customer"."name" AS varchar(15)) AS varchar(10)) FROM "customer"', + string: 'SELECT CAST(CAST("customer"."name" AS varchar(15)) AS varchar(10)) FROM "customer"' + }, + mysql: { + text : 'SELECT CAST(CAST(`customer`.`name` AS varchar(15)) AS varchar(10)) FROM `customer`', + string: 'SELECT CAST(CAST(`customer`.`name` AS varchar(15)) AS varchar(10)) FROM `customer`' + }, + mssql: { + text : 'SELECT CAST(CAST([customer].[name] AS varchar(15)) AS varchar(10)) FROM [customer]', + string: 'SELECT CAST(CAST([customer].[name] AS varchar(15)) AS varchar(10)) FROM [customer]' + }, + oracle: { + text : 'SELECT CAST(CAST("customer"."name" AS varchar(15)) AS varchar(10)) FROM "customer"', + string: 'SELECT CAST(CAST("customer"."name" AS varchar(15)) AS varchar(10)) FROM "customer"' + }, + params: [] +}); + +// Cast in WHERE for aliased columns. +Harness.test({ + query: customerAlias.select(customerAlias.name_alias).where(customerAlias.age_alias.cast('int').plus(100).equals(150)), + pg: { + text : 'SELECT "customer"."name" AS "name_alias" FROM "customer" WHERE ((CAST("customer"."age" AS int) + $1) = $2)', + string: 'SELECT "customer"."name" AS "name_alias" FROM "customer" WHERE ((CAST("customer"."age" AS int) + 100) = 150)' + }, + sqlite: { + text : 'SELECT "customer"."name" AS "name_alias" FROM "customer" WHERE ((CAST("customer"."age" AS int) + $1) = $2)', + string: 'SELECT "customer"."name" AS "name_alias" FROM "customer" WHERE ((CAST("customer"."age" AS int) + 100) = 150)' + }, + mysql: { + text : 'SELECT `customer`.`name` AS `name_alias` FROM `customer` WHERE ((CAST(`customer`.`age` AS int) + ?) = ?)', + string: 'SELECT `customer`.`name` AS `name_alias` FROM `customer` WHERE ((CAST(`customer`.`age` AS int) + 100) = 150)' + }, + mssql: { + text : 'SELECT [customer].[name] AS [name_alias] FROM [customer] WHERE ((CAST([customer].[age] AS int) + @1) = @2)', + string: 'SELECT [customer].[name] AS [name_alias] FROM [customer] WHERE ((CAST([customer].[age] AS int) + 100) = 150)' + }, + oracle: { + text : 'SELECT "customer"."name" "name_alias" FROM "customer" WHERE ((CAST("customer"."age" AS int) + :1) = :2)', + string: 'SELECT "customer"."name" "name_alias" FROM "customer" WHERE ((CAST("customer"."age" AS int) + 100) = 150)' + }, + params: [100, 150] +}); + +// Alias cast. +Harness.test({ + query: customerAlias.select(customerAlias.age_alias.cast('int').as('age_int')), + pg: { + text : 'SELECT CAST("customer"."age" AS int) AS "age_int" FROM "customer"', + string: 'SELECT CAST("customer"."age" AS int) AS "age_int" FROM "customer"' + }, + sqlite: { + text : 'SELECT CAST("customer"."age" AS int) AS "age_int" FROM "customer"', + string: 'SELECT CAST("customer"."age" AS int) AS "age_int" FROM "customer"' + }, + mysql: { + text : 'SELECT CAST(`customer`.`age` AS int) AS `age_int` FROM `customer`', + string: 'SELECT CAST(`customer`.`age` AS int) AS `age_int` FROM `customer`' + }, + mssql: { + text : 'SELECT CAST([customer].[age] AS int) AS [age_int] FROM [customer]', + string: 'SELECT CAST([customer].[age] AS int) AS [age_int] FROM [customer]' + }, + oracle: { + text : 'SELECT CAST("customer"."age" AS int) "age_int" FROM "customer"', + string: 'SELECT CAST("customer"."age" AS int) "age_int" FROM "customer"' + }, params: [] }); diff --git a/test/dialects/clause-ordering-tests.js b/test/dialects/clause-ordering-tests.js index baf7ac58..83d2d28e 100644 --- a/test/dialects/clause-ordering-tests.js +++ b/test/dialects/clause-ordering-tests.js @@ -23,6 +23,10 @@ Harness.test({ text : 'SELECT [user].[name], [post].[content] FROM [user] INNER JOIN [post] ON ([user].[id] = [post].[userId])', string: 'SELECT [user].[name], [post].[content] FROM [user] INNER JOIN [post] ON ([user].[id] = [post].[userId])' }, + 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: [] }); @@ -47,6 +51,10 @@ Harness.test({ 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: [''] }); @@ -74,6 +82,10 @@ Harness.test({ 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: [''] }); @@ -98,5 +110,9 @@ Harness.test({ text : 'SELECT [user].[id] FROM [user] WHERE ([user].[name] = @1)', string: 'SELECT [user].[id] FROM [user] WHERE ([user].[name] = \'\')' }, + oracle: { + text : 'SELECT "user"."id" FROM "user" WHERE ("user"."name" = :1)', + string: 'SELECT "user"."id" FROM "user" WHERE ("user"."name" = \'\')' + }, params: [''] }); diff --git a/test/dialects/create-table-tests.js b/test/dialects/create-table-tests.js index 9993a2fa..b1150617 100644 --- a/test/dialects/create-table-tests.js +++ b/test/dialects/create-table-tests.js @@ -33,6 +33,10 @@ Harness.test({ 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: [] }); @@ -54,6 +58,10 @@ Harness.test({ 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: [] }); @@ -82,6 +90,10 @@ Harness.test({ 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))' } }); @@ -110,6 +122,10 @@ Harness.test({ 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))' } }); @@ -139,6 +155,10 @@ Harness.test({ 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))' } }); @@ -166,6 +186,10 @@ Harness.test({ 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)' } }); @@ -189,6 +213,10 @@ Harness.test({ 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)' } }); @@ -213,6 +241,43 @@ Harness.test({ 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)' } }); @@ -224,21 +289,27 @@ Harness.test({ dataType: 'int', references: { table: 'user', - column: 'id' + column: 'id', + onDelete: 'restrict', + onUpdate: 'set null' } }] }).create(), pg: { - text : 'CREATE TABLE "post" ("userId" int REFERENCES user(id))', - string: 'CREATE TABLE "post" ("userId" int REFERENCES user(id))' + 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))', - string: 'CREATE TABLE "post" ("userId" int REFERENCES user(id))' + 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))', - string: 'CREATE TABLE `post` (`userId` int REFERENCES user(id))' + 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: [] }); @@ -260,16 +331,52 @@ Harness.test({ }] }).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))' + 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))' + 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))' + 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: [] }); @@ -295,6 +402,10 @@ Harness.test({ 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: [] }); @@ -320,3 +431,331 @@ Harness.test({ string: 'CREATE TABLE `membership` (`group_id` int, `user_id` int, `desc` varchar, PRIMARY KEY (`group_id`, `user_id`))', } }); + +// TEMPORARY TABLE TESTS + +// This tests explicitly setting the isTemporary flag to false, as opposed to all the test above here which have it +// as undefined. +Harness.test({ + query: Table.define({ + name: 'post', + columns: [{ + name: 'id', + dataType: 'int' + }], + isTemporary:false + }).create(), + pg: { + text : 'CREATE TABLE "post" ("id" int)', + string: 'CREATE TABLE "post" ("id" int)' + }, + sqlite: { + text : 'CREATE TABLE "post" ("id" int)', + string: 'CREATE TABLE "post" ("id" int)' + }, + mysql: { + text : 'CREATE TABLE `post` (`id` int)', + string: 'CREATE TABLE `post` (`id` int)' + }, + mssql: { + text : 'CREATE TABLE [post] ([id] int)', + string: 'CREATE TABLE [post] ([id] int)' + }, + oracle: { + text : 'CREATE TABLE "post" ("id" int)', + string: 'CREATE TABLE "post" ("id" int)' + }, + params: [] +}); + +Harness.test({ + query: Table.define({ + name: 'post', + columns: [{ + name: 'id', + dataType: 'int' + }], + isTemporary:true + }).create(), + pg: { + text : 'CREATE TEMPORARY TABLE "post" ("id" int)', + string: 'CREATE TEMPORARY TABLE "post" ("id" int)' + }, + sqlite: { + text : 'CREATE TEMPORARY TABLE "post" ("id" int)', + string: 'CREATE TEMPORARY TABLE "post" ("id" int)' + }, + mysql: { + text : 'CREATE TEMPORARY TABLE `post` (`id` int)', + string: 'CREATE TEMPORARY TABLE `post` (`id` int)' + }, + //mssql: { + // text : 'CREATE TABLE [#post] ([id] int)', + // string: 'CREATE TABLE [#post] ([id] int)' + //}, + params: [] +}); + +var users = Table.define({ + name: 'users', + columns: { + id: { + primaryKey: true, + dataType: 'int', + references: { + table: "entity", + column: "id", + constraint: "DEFERRABLE INITIALLY DEFERRED" + } + } + } +}); + +Harness.test({ + query: users.create(), + pg: { + text: 'CREATE TABLE "users" ("id" int PRIMARY KEY REFERENCES "entity"("id") DEFERRABLE INITIALLY DEFERRED)', + string: 'CREATE TABLE "users" ("id" int PRIMARY KEY REFERENCES "entity"("id") DEFERRABLE INITIALLY DEFERRED)' + }, + sqlite: { + text: 'CREATE TABLE "users" ("id" int PRIMARY KEY REFERENCES "entity"("id") DEFERRABLE INITIALLY DEFERRED)', + string: 'CREATE TABLE "users" ("id" int PRIMARY KEY REFERENCES "entity"("id") DEFERRABLE INITIALLY DEFERRED)' + }, + mysql: { + text: 'CREATE TABLE `users` (`id` int PRIMARY KEY REFERENCES `entity`(`id`) DEFERRABLE INITIALLY DEFERRED)', + string: 'CREATE TABLE `users` (`id` int PRIMARY KEY REFERENCES `entity`(`id`) DEFERRABLE INITIALLY DEFERRED)' + }, + mssql: { + text: 'CREATE TABLE [users] ([id] int PRIMARY KEY REFERENCES [entity]([id]) DEFERRABLE INITIALLY DEFERRED)', + string: 'CREATE TABLE [users] ([id] int PRIMARY KEY REFERENCES [entity]([id]) DEFERRABLE INITIALLY DEFERRED)' + }, + oracle: { + text: 'CREATE TABLE "users" ("id" int PRIMARY KEY REFERENCES "entity"("id") DEFERRABLE INITIALLY DEFERRED)', + string: 'CREATE TABLE "users" ("id" int PRIMARY KEY REFERENCES "entity"("id") DEFERRABLE INITIALLY DEFERRED)' + }, + params: [] +}); + +// UNIQUE COLUMN TESTS +Harness.test({ + query: Table.define({ + name: 'post', + columns: [{ + name: 'id', + dataType: 'int', + //primaryKey: true, + //notNull: true, + unique: true + }] + }).create(), + pg: { + text : 'CREATE TABLE "post" ("id" int UNIQUE)', + string: 'CREATE TABLE "post" ("id" int UNIQUE)' + }, + sqlite: { + text : 'CREATE TABLE "post" ("id" int UNIQUE)', + string: 'CREATE TABLE "post" ("id" int UNIQUE)' + }, + mysql: { + text : 'CREATE TABLE `post` (`id` int UNIQUE)', + string: 'CREATE TABLE `post` (`id` int UNIQUE)' + }, + mssql: { + text : 'CREATE TABLE [post] ([id] int UNIQUE)', + string: 'CREATE TABLE [post] ([id] int UNIQUE)' + }, + oracle: { + text : 'CREATE TABLE "post" ("id" int UNIQUE)', + string: 'CREATE TABLE "post" ("id" int UNIQUE)' + }, + params: [] +}); + + +var noUsers = Table.define({ + name: 'no_users', + columns: { + id: { + primaryKey: true, + dataType: 'int', + references: { + table: "entity", + column: "id", + constraint: "" + } + } + } +}); + +Harness.test({ + query: noUsers.create(), + pg: { + text : 'CREATE TABLE "no_users" ("id" int PRIMARY KEY REFERENCES "entity"("id"))', + string: 'CREATE TABLE "no_users" ("id" int PRIMARY KEY REFERENCES "entity"("id"))' + }, + sqlite: { + text : 'CREATE TABLE "no_users" ("id" int PRIMARY KEY REFERENCES "entity"("id"))', + string: 'CREATE TABLE "no_users" ("id" int PRIMARY KEY REFERENCES "entity"("id"))' + }, + mysql: { + text : 'CREATE TABLE `no_users` (`id` int PRIMARY KEY REFERENCES `entity`(`id`))', + string: 'CREATE TABLE `no_users` (`id` int PRIMARY KEY REFERENCES `entity`(`id`))' + }, + mssql: { + text : 'CREATE TABLE [no_users] ([id] int PRIMARY KEY REFERENCES [entity]([id]))', + string: 'CREATE TABLE [no_users] ([id] int PRIMARY KEY REFERENCES [entity]([id]))' + }, + oracle: { + text : 'CREATE TABLE "no_users" ("id" int PRIMARY KEY REFERENCES "entity"("id"))', + string: 'CREATE TABLE "no_users" ("id" int PRIMARY KEY REFERENCES "entity"("id"))' + }, + params: [] +}); + +Harness.test({ + query: Table.define({ + name: 'post', + columns: [{ + name: 'id', + dataType: 'int', + //primaryKey: true, + notNull: true, + unique: true + }] + }).create(), + pg: { + text : 'CREATE TABLE "post" ("id" int NOT NULL UNIQUE)', + string: 'CREATE TABLE "post" ("id" int NOT NULL UNIQUE)' + }, + sqlite: { + text : 'CREATE TABLE "post" ("id" int NOT NULL UNIQUE)', + string: 'CREATE TABLE "post" ("id" int NOT NULL UNIQUE)' + }, + mysql: { + text : 'CREATE TABLE `post` (`id` int NOT NULL UNIQUE)', + string: 'CREATE TABLE `post` (`id` int NOT NULL UNIQUE)' + }, + mssql: { + text : 'CREATE TABLE [post] ([id] int NOT NULL UNIQUE)', + string: 'CREATE TABLE [post] ([id] int NOT NULL UNIQUE)' + }, + oracle: { + text : 'CREATE TABLE "post" ("id" int NOT NULL UNIQUE)', + string: 'CREATE TABLE "post" ("id" int NOT NULL UNIQUE)' + }, + params: [] +}); + +Harness.test({ + query: Table.define({ + name: 'post', + columns: [{ + name: 'id', + dataType: 'int', + primaryKey: true, + //notNull: true, + unique: true + }] + }).create(), + pg: { + text : 'CREATE TABLE "post" ("id" int PRIMARY KEY)', + string: 'CREATE TABLE "post" ("id" int PRIMARY KEY)' + }, + sqlite: { + text : 'CREATE TABLE "post" ("id" int PRIMARY KEY)', + string: 'CREATE TABLE "post" ("id" int PRIMARY KEY)' + }, + mysql: { + text : 'CREATE TABLE `post` (`id` int PRIMARY KEY)', + string: 'CREATE TABLE `post` (`id` int PRIMARY KEY)' + }, + mssql: { + text : 'CREATE TABLE [post] ([id] int PRIMARY KEY)', + string: 'CREATE TABLE [post] ([id] int PRIMARY KEY)' + }, + oracle: { + text : 'CREATE TABLE "post" ("id" int PRIMARY KEY)', + string: 'CREATE TABLE "post" ("id" int PRIMARY KEY)' + }, + params: [] +}); + +Harness.test({ + query: Table.define({ + name: 'post', + columns: [{ + name: 'id', + dataType: 'int', + primaryKey: true + }, { + name: 'blog_id', + dataType: 'int' + }, { + name: 'user_id', + dataType: 'int' + }], + foreignKeys: { + table: 'users', + columns: [ 'blog_id', 'user_id' ], + refColumns: [ 'id', 'user_id' ] + } + }).create(), + pg: { + text : 'CREATE TABLE "post" ("id" int PRIMARY KEY, "blog_id" int, "user_id" int, FOREIGN KEY ( "blog_id", "user_id" ) REFERENCES "users" ( "id", "user_id" ))', + string: 'CREATE TABLE "post" ("id" int PRIMARY KEY, "blog_id" int, "user_id" int, FOREIGN KEY ( "blog_id", "user_id" ) REFERENCES "users" ( "id", "user_id" ))' + }, + sqlite: { + text : 'CREATE TABLE "post" ("id" int PRIMARY KEY, "blog_id" int, "user_id" int, FOREIGN KEY ( "blog_id", "user_id" ) REFERENCES "users" ( "id", "user_id" ))', + string: 'CREATE TABLE "post" ("id" int PRIMARY KEY, "blog_id" int, "user_id" int, FOREIGN KEY ( "blog_id", "user_id" ) REFERENCES "users" ( "id", "user_id" ))' + }, + mysql: { + text : 'CREATE TABLE `post` (`id` int PRIMARY KEY, `blog_id` int, `user_id` int, FOREIGN KEY ( `blog_id`, `user_id` ) REFERENCES `users` ( `id`, `user_id` ))', + string: 'CREATE TABLE `post` (`id` int PRIMARY KEY, `blog_id` int, `user_id` int, FOREIGN KEY ( `blog_id`, `user_id` ) REFERENCES `users` ( `id`, `user_id` ))' + }, + params: [] +}); + +Harness.test({ + query: Table.define({ + name: 'replies', + columns: [{ + name: 'id', + dataType: 'int', + primaryKey: true + }, { + name: 'blog_id', + dataType: 'int' + }, { + name: 'post_id', + dataType: 'int' + }, { + name: 'user_id', + dataType: 'int' + }], + foreignKeys: [{ + table: 'users', + columns: [ 'blog_id', 'user_id' ], + onDelete: 'no action' + }, { + name: 'posts_idx', + table: 'posts', + columns: [ 'blog_id', 'post_id' ], + refColumns: [ 'blog_id', 'id' ], + onDelete: 'cascade', + onUpdate: 'set default' + }] + }).create(), + pg: { + text : 'CREATE TABLE "replies" ("id" int PRIMARY KEY, "blog_id" int, "post_id" int, "user_id" int, FOREIGN KEY ( "blog_id", "user_id" ) REFERENCES "users" ON DELETE NO ACTION, CONSTRAINT "posts_idx" FOREIGN KEY ( "blog_id", "post_id" ) REFERENCES "posts" ( "blog_id", "id" ) ON DELETE CASCADE ON UPDATE SET DEFAULT)', + string: 'CREATE TABLE "replies" ("id" int PRIMARY KEY, "blog_id" int, "post_id" int, "user_id" int, FOREIGN KEY ( "blog_id", "user_id" ) REFERENCES "users" ON DELETE NO ACTION, CONSTRAINT "posts_idx" FOREIGN KEY ( "blog_id", "post_id" ) REFERENCES "posts" ( "blog_id", "id" ) ON DELETE CASCADE ON UPDATE SET DEFAULT)' + }, + sqlite: { + text : 'CREATE TABLE "replies" ("id" int PRIMARY KEY, "blog_id" int, "post_id" int, "user_id" int, FOREIGN KEY ( "blog_id", "user_id" ) REFERENCES "users" ON DELETE NO ACTION, CONSTRAINT "posts_idx" FOREIGN KEY ( "blog_id", "post_id" ) REFERENCES "posts" ( "blog_id", "id" ) ON DELETE CASCADE ON UPDATE SET DEFAULT)', + string: 'CREATE TABLE "replies" ("id" int PRIMARY KEY, "blog_id" int, "post_id" int, "user_id" int, FOREIGN KEY ( "blog_id", "user_id" ) REFERENCES "users" ON DELETE NO ACTION, CONSTRAINT "posts_idx" FOREIGN KEY ( "blog_id", "post_id" ) REFERENCES "posts" ( "blog_id", "id" ) ON DELETE CASCADE ON UPDATE SET DEFAULT)' + }, + mysql: { + text : 'CREATE TABLE `replies` (`id` int PRIMARY KEY, `blog_id` int, `post_id` int, `user_id` int, FOREIGN KEY ( `blog_id`, `user_id` ) REFERENCES `users` ON DELETE NO ACTION, CONSTRAINT `posts_idx` FOREIGN KEY ( `blog_id`, `post_id` ) REFERENCES `posts` ( `blog_id`, `id` ) ON DELETE CASCADE ON UPDATE SET DEFAULT)', + string: 'CREATE TABLE `replies` (`id` int PRIMARY KEY, `blog_id` int, `post_id` int, `user_id` int, FOREIGN KEY ( `blog_id`, `user_id` ) REFERENCES `users` ON DELETE NO ACTION, CONSTRAINT `posts_idx` FOREIGN KEY ( `blog_id`, `post_id` ) REFERENCES `posts` ( `blog_id`, `id` ) ON DELETE CASCADE ON UPDATE SET DEFAULT)' + }, + params: [] +}); diff --git a/test/dialects/create-view-tests.js b/test/dialects/create-view-tests.js new file mode 100644 index 00000000..977f540a --- /dev/null +++ b/test/dialects/create-view-tests.js @@ -0,0 +1,80 @@ +'use strict'; + +var Harness = require('./support'); +var user = Harness.defineUserTable(); + +//simple view create +Harness.test({ + query: user.select(user.star()).createView('allUsersView'), + pg: { + text : '(CREATE VIEW "allUsersView" AS SELECT "user".* FROM "user")', + string: '(CREATE VIEW "allUsersView" AS SELECT "user".* FROM "user")' + }, + sqlite: { + text : '(CREATE VIEW "allUsersView" AS SELECT "user".* FROM "user")', + string: '(CREATE VIEW "allUsersView" AS SELECT "user".* FROM "user")' + }, + mysql: { + text : '(CREATE VIEW `allUsersView` AS SELECT `user`.* FROM `user`)', + string: '(CREATE VIEW `allUsersView` AS SELECT `user`.* FROM `user`)' + }, + mssql: { + text : '(CREATE VIEW [allUsersView] AS SELECT [user].* FROM [user])', + string: '(CREATE VIEW [allUsersView] AS SELECT [user].* FROM [user])' + }, + oracle: { + text : '(CREATE VIEW "allUsersView" AS SELECT "user".* FROM "user")', + string: '(CREATE VIEW "allUsersView" AS SELECT "user".* FROM "user")' + } +}); + +//create view with parameters +Harness.test({ + query: user.select(user.star()).where(user.id.equals(1)).createView('oneUserView'), + pg: { + text : '(CREATE VIEW "oneUserView" AS SELECT "user".* FROM "user" WHERE ("user"."id" = 1))', + string: '(CREATE VIEW "oneUserView" AS SELECT "user".* FROM "user" WHERE ("user"."id" = 1))' + }, + sqlite: { + text : '(CREATE VIEW "oneUserView" AS SELECT "user".* FROM "user" WHERE ("user"."id" = 1))', + string: '(CREATE VIEW "oneUserView" AS SELECT "user".* FROM "user" WHERE ("user"."id" = 1))' + }, + mysql: { + text : '(CREATE VIEW `oneUserView` AS SELECT `user`.* FROM `user` WHERE (`user`.`id` = 1))', + string: '(CREATE VIEW `oneUserView` AS SELECT `user`.* FROM `user` WHERE (`user`.`id` = 1))' + }, + mssql: { + text : '(CREATE VIEW [oneUserView] AS SELECT [user].* FROM [user] WHERE ([user].[id] = 1))', + string: '(CREATE VIEW [oneUserView] AS SELECT [user].* FROM [user] WHERE ([user].[id] = 1))' + }, + oracle: { + text : '(CREATE VIEW "oneUserView" AS SELECT "user".* FROM "user" WHERE ("user"."id" = 1))', + string: '(CREATE VIEW "oneUserView" AS SELECT "user".* FROM "user" WHERE ("user"."id" = 1))' + } +}); + +//Tests error raised for non-SELECT create view attempts +Harness.test({ + query: user.delete().where(user.id.equals(1)).createView('oneUserView'), + pg: { + text : 'Create View requires a Select.', + throws: true + }, + sqlite: { + text : 'Create View requires a Select.', + throws: true + }, + mysql: { + text : 'Create View requires a Select.', + throws: true + }, + mssql: { + text : 'Create View requires a Select.', + throws: true + }, + oracle: { + text : 'Create View requires a Select.', + throws: true + }, + params: [] +}); diff --git a/test/dialects/date-tests.js b/test/dialects/date-tests.js new file mode 100644 index 00000000..dd2c4d64 --- /dev/null +++ b/test/dialects/date-tests.js @@ -0,0 +1,228 @@ +'use strict'; + +var Harness = require('./support'); +var customer = Harness.defineCustomerTable(); +var Sql = require('../../lib'); + +Harness.test({ + query: customer.select(Sql.functions.YEAR(customer.metadata)), + pg: { + text : 'SELECT EXTRACT(YEAR FROM "customer"."metadata") FROM "customer"', + string: 'SELECT EXTRACT(YEAR FROM "customer"."metadata") FROM "customer"' + }, + sqlite: { + text : 'SELECT strftime(\'%Y\', "customer"."metadata") FROM "customer"', + string: 'SELECT strftime(\'%Y\', "customer"."metadata") FROM "customer"' + }, + mysql: { + text : 'SELECT YEAR(`customer`.`metadata`) FROM `customer`', + string: 'SELECT YEAR(`customer`.`metadata`) FROM `customer`' + }, + mssql: { + text : 'SELECT DATEPART(year, [customer].[metadata]) FROM [customer]', + string: 'SELECT DATEPART(year, [customer].[metadata]) FROM [customer]' + }, + oracle: { + text : 'SELECT EXTRACT(YEAR FROM "customer"."metadata") FROM "customer"', + string: 'SELECT EXTRACT(YEAR FROM "customer"."metadata") FROM "customer"' + }, + params: [] +}); + +Harness.test({ + query: customer.select(Sql.functions.MONTH(customer.metadata)), + pg: { + text : 'SELECT EXTRACT(MONTH FROM "customer"."metadata") FROM "customer"', + string: 'SELECT EXTRACT(MONTH FROM "customer"."metadata") FROM "customer"' + }, + sqlite: { + text: 'SELECT strftime(\'%m\', datetime("customer"."metadata"/1000, "unixepoch")) FROM "customer"', + string: 'SELECT strftime(\'%m\', datetime("customer"."metadata"/1000, "unixepoch")) FROM "customer"', + config: { + dateTimeMillis: true + } + }, + mysql: { + text : 'SELECT MONTH(`customer`.`metadata`) FROM `customer`', + string: 'SELECT MONTH(`customer`.`metadata`) FROM `customer`' + }, + mssql: { + text : 'SELECT DATEPART(month, [customer].[metadata]) FROM [customer]', + string: 'SELECT DATEPART(month, [customer].[metadata]) FROM [customer]' + }, + oracle: { + text : 'SELECT EXTRACT(MONTH FROM "customer"."metadata") FROM "customer"', + string: 'SELECT EXTRACT(MONTH FROM "customer"."metadata") FROM "customer"' + }, + params: [] +}); + +Harness.test({ + query: customer.select(Sql.functions.DAY(customer.metadata)), + pg: { + text : 'SELECT EXTRACT(DAY FROM "customer"."metadata") FROM "customer"', + string: 'SELECT EXTRACT(DAY FROM "customer"."metadata") FROM "customer"' + }, + sqlite: { + text : 'SELECT strftime(\'%d\', "customer"."metadata") FROM "customer"', + string: 'SELECT strftime(\'%d\', "customer"."metadata") FROM "customer"' + }, + mysql: { + text : 'SELECT DAY(`customer`.`metadata`) FROM `customer`', + string: 'SELECT DAY(`customer`.`metadata`) FROM `customer`' + }, + mssql: { + text : 'SELECT DATEPART(day, [customer].[metadata]) FROM [customer]', + string: 'SELECT DATEPART(day, [customer].[metadata]) FROM [customer]' + }, + oracle: { + text : 'SELECT EXTRACT(DAY FROM "customer"."metadata") FROM "customer"', + string: 'SELECT EXTRACT(DAY FROM "customer"."metadata") FROM "customer"' + }, + params: [] +}); + +Harness.test({ + query: customer.select(Sql.functions.HOUR(customer.metadata)), + pg: { + text : 'SELECT EXTRACT(HOUR FROM "customer"."metadata") FROM "customer"', + string: 'SELECT EXTRACT(HOUR FROM "customer"."metadata") FROM "customer"' + }, + sqlite: { + text: 'SELECT strftime(\'%H\', datetime("customer"."metadata"/1000, "unixepoch")) FROM "customer"', + string: 'SELECT strftime(\'%H\', datetime("customer"."metadata"/1000, "unixepoch")) FROM "customer"', + config: { + dateTimeMillis: true + } + }, + mysql: { + text : 'SELECT HOUR(`customer`.`metadata`) FROM `customer`', + string: 'SELECT HOUR(`customer`.`metadata`) FROM `customer`' + }, + mssql: { + text : 'SELECT DATEPART(hour, [customer].[metadata]) FROM [customer]', + string: 'SELECT DATEPART(hour, [customer].[metadata]) FROM [customer]' + }, + oracle: { + text : 'SELECT EXTRACT(HOUR FROM "customer"."metadata") FROM "customer"', + string: 'SELECT EXTRACT(HOUR FROM "customer"."metadata") FROM "customer"' + }, + params: [] +}); + +Harness.test({ + query: customer.select(Sql.functions.CURRENT_TIMESTAMP()), + pg: { + text : 'SELECT CURRENT_TIMESTAMP FROM "customer"', + string: 'SELECT CURRENT_TIMESTAMP FROM "customer"' + }, + sqlite: { + text : 'SELECT CURRENT_TIMESTAMP FROM "customer"', + string: 'SELECT CURRENT_TIMESTAMP FROM "customer"' + }, + mysql: { + text : 'SELECT CURRENT_TIMESTAMP FROM `customer`', + string: 'SELECT CURRENT_TIMESTAMP FROM `customer`' + }, + mssql: { + text : 'SELECT CURRENT_TIMESTAMP FROM [customer]', + string: 'SELECT CURRENT_TIMESTAMP FROM [customer]' + }, + oracle: { + text : 'SELECT CURRENT_TIMESTAMP FROM "customer"', + string: 'SELECT CURRENT_TIMESTAMP FROM "customer"' + }, + params: [] +}); + +Harness.test({ + query: Sql.select(Sql.functions.CURRENT_TIMESTAMP().plus(Sql.interval({hours:1}))), + pg: { + text : 'SELECT (CURRENT_TIMESTAMP + INTERVAL \'1 HOUR\')', + string: 'SELECT (CURRENT_TIMESTAMP + INTERVAL \'1 HOUR\')' + }, + mysql: { + text : 'SELECT (CURRENT_TIMESTAMP + INTERVAL \'1:0:0\' HOUR_SECOND)', + string: 'SELECT (CURRENT_TIMESTAMP + INTERVAL \'1:0:0\' HOUR_SECOND)' + }, + params: [] +}); + +Harness.test({ + query: Sql.select(Sql.functions.CURRENT_TIMESTAMP().minus(Sql.interval({years:3}))), + pg: { + text : 'SELECT (CURRENT_TIMESTAMP - INTERVAL \'3 YEAR\')', + string: 'SELECT (CURRENT_TIMESTAMP - INTERVAL \'3 YEAR\')' + }, + mysql: { + text : 'SELECT (CURRENT_TIMESTAMP - INTERVAL 3 YEAR)', + string: 'SELECT (CURRENT_TIMESTAMP - INTERVAL 3 YEAR)' + }, + params: [] +}); + +Harness.test({ + query: Sql.select(Sql.functions.CURRENT_TIMESTAMP().minus(Sql.interval({years:3, months:2}))), + pg: { + text : 'SELECT (CURRENT_TIMESTAMP - INTERVAL \'3 YEAR 2 MONTH\')', + string: 'SELECT (CURRENT_TIMESTAMP - INTERVAL \'3 YEAR 2 MONTH\')' + }, + mysql: { + text : 'SELECT (CURRENT_TIMESTAMP - INTERVAL \'3-2\' YEAR_MONTH)', + string: 'SELECT (CURRENT_TIMESTAMP - INTERVAL \'3-2\' YEAR_MONTH)' + }, + params: [] +}); + +Harness.test({ + query: Sql.select(Sql.functions.CURRENT_TIMESTAMP().plus(Sql.interval({hours:1, minutes:20}))), + pg: { + text : 'SELECT (CURRENT_TIMESTAMP + INTERVAL \'1 HOUR 20 MINUTE\')', + string: 'SELECT (CURRENT_TIMESTAMP + INTERVAL \'1 HOUR 20 MINUTE\')' + }, + mysql: { + text : 'SELECT (CURRENT_TIMESTAMP + INTERVAL \'1:20:0\' HOUR_SECOND)', + string: 'SELECT (CURRENT_TIMESTAMP + INTERVAL \'1:20:0\' HOUR_SECOND)' + }, + params: [] +}); + +Harness.test({ + query: Sql.select(Sql.functions.CURRENT_TIMESTAMP().plus(Sql.interval({hours:'sql\'injection', minutes:20}))), + pg: { + text : 'SELECT (CURRENT_TIMESTAMP + INTERVAL \'20 MINUTE\')', + string: 'SELECT (CURRENT_TIMESTAMP + INTERVAL \'20 MINUTE\')' + }, + mysql: { + text : 'SELECT (CURRENT_TIMESTAMP + INTERVAL \'0:20:0\' HOUR_SECOND)', + string: 'SELECT (CURRENT_TIMESTAMP + INTERVAL \'0:20:0\' HOUR_SECOND)' + }, + params: [] +}); + +Harness.test({ + query: Sql.select(Sql.functions.CURRENT_TIMESTAMP().minus(Sql.interval({days: 1, hours:5, minutes: 'sql\'injection'}))), + pg: { + text : 'SELECT (CURRENT_TIMESTAMP - INTERVAL \'1 DAY 5 HOUR\')', + string: 'SELECT (CURRENT_TIMESTAMP - INTERVAL \'1 DAY 5 HOUR\')' + }, + mysql: { + text : 'SELECT (CURRENT_TIMESTAMP - INTERVAL \'1 5:0:0\' DAY_SECOND)', + string: 'SELECT (CURRENT_TIMESTAMP - INTERVAL \'1 5:0:0\' DAY_SECOND)' + }, + params: [] +}); + +Harness.test({ + query: Sql.select(Sql.functions.CURRENT_TIMESTAMP().minus(Sql.interval({years: 2, months: 5}))), + pg: { + text : 'SELECT (CURRENT_TIMESTAMP - INTERVAL \'2 YEAR 5 MONTH\')', + string: 'SELECT (CURRENT_TIMESTAMP - INTERVAL \'2 YEAR 5 MONTH\')' + }, + mysql: { + text : 'SELECT (CURRENT_TIMESTAMP - INTERVAL \'2-5\' YEAR_MONTH)', + string: 'SELECT (CURRENT_TIMESTAMP - INTERVAL \'2-5\' YEAR_MONTH)' + }, + params: [] +}); + diff --git a/test/dialects/delete-tests.js b/test/dialects/delete-tests.js index 3e1a3915..51fe3026 100644 --- a/test/dialects/delete-tests.js +++ b/test/dialects/delete-tests.js @@ -22,6 +22,10 @@ Harness.test({ 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"] }); @@ -76,6 +80,10 @@ Harness.test({ 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' ] }); @@ -99,6 +107,10 @@ Harness.test({ 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: [''] }); @@ -122,6 +134,10 @@ Harness.test({ 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: [''] }); @@ -145,5 +161,9 @@ Harness.test({ text : 'DELETE FROM [post] WHERE (([post].[content] = @1) OR ([post].[content] IS NULL))', string: "DELETE FROM [post] WHERE (([post].[content] = '') OR ([post].[content] IS NULL))" }, + oracle: { + text : 'DELETE FROM "post" WHERE (("post"."content" = :1) OR ("post"."content" IS NULL))', + string: 'DELETE FROM "post" WHERE (("post"."content" = \'\') OR ("post"."content" IS NULL))' + }, params: [''] }); diff --git a/test/dialects/distinct-on-tests.js b/test/dialects/distinct-on-tests.js new file mode 100644 index 00000000..d238f928 --- /dev/null +++ b/test/dialects/distinct-on-tests.js @@ -0,0 +1,24 @@ +'use strict'; + +var Harness = require('./support'); +var user = Harness.defineUserTable(); +var Sql = require('../../lib').setDialect('postgres'); + +Harness.test({ + query: user.select().distinctOn(user.id), + pg: { + text : 'SELECT DISTINCT ON("user"."id") "user".* FROM "user"', + string: 'SELECT DISTINCT ON("user"."id") "user".* FROM "user"' + }, + params: [] +}); + +Harness.test({ + query: user.select(user.id,user.name).distinctOn(user.id), + pg: { + text : 'SELECT DISTINCT ON("user"."id") "user"."id", "user"."name" FROM "user"', + string: 'SELECT DISTINCT ON("user"."id") "user"."id", "user"."name" FROM "user"' + }, + params: [] +}); + diff --git a/test/dialects/distinct-tests.js b/test/dialects/distinct-tests.js index 97635b9d..9f922bff 100644 --- a/test/dialects/distinct-tests.js +++ b/test/dialects/distinct-tests.js @@ -21,6 +21,10 @@ Harness.test({ 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: [] }); @@ -42,5 +46,87 @@ Harness.test({ text : 'SELECT COUNT(DISTINCT([user].[id])) AS [count] FROM [user]', string: 'SELECT COUNT(DISTINCT([user].[id])) AS [count] FROM [user]' }, + oracle: { + text : 'SELECT COUNT(DISTINCT("user"."id")) "count" FROM "user"', + string: 'SELECT COUNT(DISTINCT("user"."id")) "count" FROM "user"' + }, params: [] }); + +// BELOW HERE TEST DISTINCT ON THE ENTIRE RESULTS SET, NOT JUST ONE COLUMN + +Harness.test({ + query: user.select().distinct(), + pg: { + text : 'SELECT DISTINCT "user".* FROM "user"', + string: 'SELECT DISTINCT "user".* FROM "user"' + }, + sqlite: { + text : 'SELECT DISTINCT "user".* FROM "user"', + string: 'SELECT DISTINCT "user".* FROM "user"' + }, + mysql: { + text : 'SELECT DISTINCT `user`.* FROM `user`', + string: 'SELECT DISTINCT `user`.* FROM `user`' + }, + mssql: { + text : 'SELECT DISTINCT [user].* FROM [user]', + string: 'SELECT DISTINCT [user].* FROM [user]' + }, + oracle: { + text : 'SELECT DISTINCT "user".* FROM "user"', + string: 'SELECT DISTINCT "user".* FROM "user"' + }, + params: [] +}); + +Harness.test({ + query: user.select(user.id).distinct(), + pg: { + text : 'SELECT DISTINCT "user"."id" FROM "user"', + string: 'SELECT DISTINCT "user"."id" FROM "user"' + }, + sqlite: { + text : 'SELECT DISTINCT "user"."id" FROM "user"', + string: 'SELECT DISTINCT "user"."id" FROM "user"' + }, + mysql: { + text : 'SELECT DISTINCT `user`.`id` FROM `user`', + string: 'SELECT DISTINCT `user`.`id` FROM `user`' + }, + mssql: { + text : 'SELECT DISTINCT [user].[id] FROM [user]', + string: 'SELECT DISTINCT [user].[id] FROM [user]' + }, + oracle: { + text : 'SELECT DISTINCT "user"."id" FROM "user"', + string: 'SELECT DISTINCT "user"."id" FROM "user"' + }, + params: [] +}); + +Harness.test({ + query: user.select(user.id,user.name).distinct(), + pg: { + text : 'SELECT DISTINCT "user"."id", "user"."name" FROM "user"', + string: 'SELECT DISTINCT "user"."id", "user"."name" FROM "user"' + }, + sqlite: { + text : 'SELECT DISTINCT "user"."id", "user"."name" FROM "user"', + string: 'SELECT DISTINCT "user"."id", "user"."name" FROM "user"' + }, + mysql: { + text : 'SELECT DISTINCT `user`.`id`, `user`.`name` FROM `user`', + string: 'SELECT DISTINCT `user`.`id`, `user`.`name` FROM `user`' + }, + mssql: { + text : 'SELECT DISTINCT [user].[id], [user].[name] FROM [user]', + string: 'SELECT DISTINCT [user].[id], [user].[name] FROM [user]' + }, + oracle: { + text : 'SELECT DISTINCT "user"."id", "user"."name" FROM "user"', + string: 'SELECT DISTINCT "user"."id", "user"."name" FROM "user"' + }, + params: [] +}); + diff --git a/test/dialects/drop-table-tests.js b/test/dialects/drop-table-tests.js index 7812ac69..eb183e64 100644 --- a/test/dialects/drop-table-tests.js +++ b/test/dialects/drop-table-tests.js @@ -21,6 +21,10 @@ Harness.test({ text : 'DROP TABLE [post]', string: 'DROP TABLE [post]' }, + oracle: { + text : 'DROP TABLE "post"', + string: 'DROP TABLE "post"' + }, params: [] }); @@ -42,6 +46,10 @@ Harness.test({ 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: [] }); @@ -59,6 +67,10 @@ Harness.test({ text : 'DROP TABLE `post` CASCADE', string: 'DROP TABLE `post` CASCADE' }, + oracle: { + text : 'DROP TABLE "post" CASCADE CONSTRAINTS', + string: 'DROP TABLE "post" CASCADE CONSTRAINTS' + }, params: [] }); @@ -76,5 +88,9 @@ Harness.test({ 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/from-clause-tests.js b/test/dialects/from-clause-tests.js index 2e486747..673087db 100644 --- a/test/dialects/from-clause-tests.js +++ b/test/dialects/from-clause-tests.js @@ -21,6 +21,10 @@ Harness.test({ 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"' } }); @@ -41,6 +45,10 @@ Harness.test({ 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"' } }); @@ -57,6 +65,10 @@ Harness.test({ 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"' } }); @@ -73,5 +85,9 @@ Harness.test({ mysql: { text : 'SELECT `user`.* FROM `user` , `post`', string: 'SELECT `user`.* FROM `user` , `post`' + }, + oracle: { + text : 'SELECT "user".* FROM "user" , "post"', + string: 'SELECT "user".* FROM "user" , "post"' } }); \ No newline at end of file diff --git a/test/dialects/function-tests.js b/test/dialects/function-tests.js new file mode 100644 index 00000000..702d8c63 --- /dev/null +++ b/test/dialects/function-tests.js @@ -0,0 +1,82 @@ +'use strict'; + +var Harness = require('./support'); +var Sql = require('../../lib'); + +var post = Harness.definePostTable(); + +Harness.test({ + query: post.select(Sql.functions.LENGTH(post.content)), + pg: { + text : 'SELECT LENGTH("post"."content") FROM "post"', + string: 'SELECT LENGTH("post"."content") FROM "post"' + }, + sqlite: { + text : 'SELECT LENGTH("post"."content") FROM "post"', + string: 'SELECT LENGTH("post"."content") FROM "post"' + }, + mysql: { + text : 'SELECT LENGTH(`post`.`content`) FROM `post`', + string: 'SELECT LENGTH(`post`.`content`) FROM `post`' + }, + mssql: { + text : 'SELECT LEN([post].[content]) FROM [post]', + string: 'SELECT LEN([post].[content]) FROM [post]' + }, + oracle: { + text : 'SELECT LENGTH("post"."content") FROM "post"', + string: 'SELECT LENGTH("post"."content") FROM "post"' + }, + params: [] +}); + +Harness.test({ + query: post.select(Sql.functions.LEFT(post.content,4)), + pg: { + text : 'SELECT LEFT("post"."content", $1) FROM "post"', + string: 'SELECT LEFT("post"."content", 4) FROM "post"' + }, + sqlite: { + text : 'SELECT SUBSTR("post"."content", 1, $1) FROM "post"', + string: 'SELECT SUBSTR("post"."content", 1, 4) FROM "post"' + }, + mysql: { + text : 'SELECT LEFT(`post`.`content`, ?) FROM `post`', + string: 'SELECT LEFT(`post`.`content`, 4) FROM `post`' + }, + mssql: { + text : 'SELECT LEFT([post].[content], @1) FROM [post]', + string: 'SELECT LEFT([post].[content], 4) FROM [post]' + }, + oracle: { + text : 'SELECT LEFT("post"."content", :1) FROM "post"', + string: 'SELECT LEFT("post"."content", 4) FROM "post"' + }, + params: [4] +}); + +Harness.test({ + query: post.select(Sql.functions.RIGHT(post.content,4)), + pg: { + text : 'SELECT RIGHT("post"."content", $1) FROM "post"', + string: 'SELECT RIGHT("post"."content", 4) FROM "post"' + }, + sqlite: { + text : 'SELECT SUBSTR("post"."content", -$1) FROM "post"', + string: 'SELECT SUBSTR("post"."content", -4) FROM "post"' + }, + mysql: { + text : 'SELECT RIGHT(`post`.`content`, ?) FROM `post`', + string: 'SELECT RIGHT(`post`.`content`, 4) FROM `post`' + }, + mssql: { + text : 'SELECT RIGHT([post].[content], @1) FROM [post]', + string: 'SELECT RIGHT([post].[content], 4) FROM [post]' + }, + oracle: { + text : 'SELECT RIGHT("post"."content", :1) FROM "post"', + string: 'SELECT RIGHT("post"."content", 4) FROM "post"' + }, + params: [4] +}); + diff --git a/test/dialects/group-by-tests.js b/test/dialects/group-by-tests.js index d0d4c1eb..673b5639 100644 --- a/test/dialects/group-by-tests.js +++ b/test/dialects/group-by-tests.js @@ -21,6 +21,10 @@ Harness.test({ 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: [] }); @@ -42,6 +46,10 @@ Harness.test({ text : 'SELECT [post].[content] FROM [post] GROUP BY [post].[userId], [post].[id]', string: 'SELECT [post].[content] FROM [post] GROUP BY [post].[userId], [post].[id]' }, + 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: [] }); @@ -63,6 +71,10 @@ Harness.test({ text : 'SQL Server does not support array_agg.', throws: true }, + oracle: { + text : 'Oracle does not support array_agg.', + throws: true + }, params: [] }); @@ -84,6 +96,10 @@ Harness.test({ text : 'SQL Server does not support array_agg.', throws: true }, + oracle: { + text : 'Oracle does not support array_agg.', + throws: true + }, params: [] }); @@ -105,5 +121,9 @@ Harness.test({ text : 'SELECT [post].[content] FROM [post] GROUP BY [post].[userId], [post].[id]', string: 'SELECT [post].[content] FROM [post] GROUP BY [post].[userId], [post].[id]' }, + oracel: { + text : 'SELECT "post"."content" FROM "post" GROUP BY "post"."userId", "post"."id"', + string: 'SELECT "post"."content" FROM "post" GROUP BY "post"."userId", "post"."id"' + }, params: [] }); diff --git a/test/dialects/having-tests.js b/test/dialects/having-tests.js index ad315c6e..5f8f9cc7 100644 --- a/test/dialects/having-tests.js +++ b/test/dialects/having-tests.js @@ -21,6 +21,10 @@ Harness.test({ 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] }); @@ -42,6 +46,10 @@ Harness.test({ 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] }); @@ -63,5 +71,9 @@ Harness.test({ 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/in-clause-tests.js b/test/dialects/in-clause-tests.js index 374ca6fd..513c7243 100644 --- a/test/dialects/in-clause-tests.js +++ b/test/dialects/in-clause-tests.js @@ -21,6 +21,10 @@ Harness.test({ 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: [] }); @@ -42,6 +46,10 @@ Harness.test({ 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] }); @@ -63,6 +71,10 @@ Harness.test({ 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: [] }); @@ -84,6 +96,10 @@ Harness.test({ 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] }); @@ -105,6 +121,10 @@ Harness.test({ 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: [] }); @@ -126,6 +146,10 @@ Harness.test({ 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] }); @@ -147,5 +171,9 @@ Harness.test({ text : 'SELECT [post].* FROM [post] WHERE ([post].[id] IN (@1, @2) OR [post].[id] IS NULL)', string: 'SELECT [post].* FROM [post] WHERE ([post].[id] IN (1, 2) OR [post].[id] IS NULL)' }, + oracle: { + text : 'SELECT "post".* FROM "post" WHERE ("post"."id" IN (:1, :2) OR "post"."id" IS NULL)', + string: 'SELECT "post".* FROM "post" WHERE ("post"."id" IN (1, 2) OR "post"."id" IS NULL)' + }, params: [1, 2] }); diff --git a/test/dialects/indexes-tests.js b/test/dialects/indexes-tests.js index 482e0d6d..86b50449 100644 --- a/test/dialects/indexes-tests.js +++ b/test/dialects/indexes-tests.js @@ -17,6 +17,10 @@ Harness.test({ text : 'PRAGMA INDEX_LIST("post")', string: 'PRAGMA INDEX_LIST("post")' }, + oracle: { + text : 'SELECT * FROM USER_INDEXES WHERE TABLE_NAME = \'post\'', + string: 'SELECT * FROM USER_INDEXES WHERE TABLE_NAME = \'post\'' + }, params: [] }); @@ -34,6 +38,10 @@ Harness.test({ text : 'CREATE UNIQUE INDEX "index_name" USING BTREE ON "post" ("id","userId") WITH PARSER foo', string: 'CREATE UNIQUE INDEX "index_name" USING BTREE ON "post" ("id","userId") WITH PARSER foo' }, + oracle: { + text : 'CREATE UNIQUE INDEX "index_name" USING BTREE ON "post" ("id","userId") WITH PARSER foo', + string: 'CREATE UNIQUE INDEX "index_name" USING BTREE ON "post" ("id","userId") WITH PARSER foo' + }, params: [] }); @@ -85,6 +93,10 @@ Harness.test({ text : 'CREATE INDEX "post_id_userId" ON "post" ("userId","id")', string: 'CREATE INDEX "post_id_userId" ON "post" ("userId","id")' }, + oracle: { + text : 'CREATE INDEX "post_id_userId" ON "post" ("userId","id")', + string: 'CREATE INDEX "post_id_userId" ON "post" ("userId","id")' + }, params: [] }); @@ -102,9 +114,64 @@ Harness.test({ text : 'CREATE INDEX "post_id_userId" ON "post" ("userId","id")', string: 'CREATE INDEX "post_id_userId" ON "post" ("userId","id")' }, + oracle: { + text : 'CREATE INDEX "post_id_userId" ON "post" ("userId","id")', + string: 'CREATE INDEX "post_id_userId" ON "post" ("userId","id")' + }, + params: [] +}); + +Harness.test({ + query: post.indexes().create().on(post.userId, post.id.desc), + pg: { + text : 'CREATE INDEX "post_id_userId" ON "post" ("userId","id" DESC)', + string: 'CREATE INDEX "post_id_userId" ON "post" ("userId","id" DESC)' + }, + mysql: { + text : 'CREATE INDEX `post_id_userId` ON `post` (`userId`,`id` DESC)', + string: 'CREATE INDEX `post_id_userId` ON `post` (`userId`,`id` DESC)' + }, + sqlite: { + text : 'CREATE INDEX "post_id_userId" ON "post" ("userId","id" DESC)', + string: 'CREATE INDEX "post_id_userId" ON "post" ("userId","id" DESC)' + }, + mssql: { + text : 'CREATE INDEX [post_id_userId] ON [post] ([userId],[id] DESC)', + string: 'CREATE INDEX [post_id_userId] ON [post] ([userId],[id] DESC)' + }, + oracle: { + text : 'CREATE INDEX "post_id_userId" ON "post" ("userId","id" DESC)', + string: 'CREATE INDEX "post_id_userId" ON "post" ("userId","id" DESC)' + }, + params: [] +}); + +Harness.test({ + query: post.indexes().create().on(post.userId).on(post.id.descending), + pg: { + text : 'CREATE INDEX "post_id_userId" ON "post" ("userId","id" DESC)', + string: 'CREATE INDEX "post_id_userId" ON "post" ("userId","id" DESC)' + }, + mysql: { + text : 'CREATE INDEX `post_id_userId` ON `post` (`userId`,`id` DESC)', + string: 'CREATE INDEX `post_id_userId` ON `post` (`userId`,`id` DESC)' + }, + sqlite: { + text : 'CREATE INDEX "post_id_userId" ON "post" ("userId","id" DESC)', + string: 'CREATE INDEX "post_id_userId" ON "post" ("userId","id" DESC)' + }, + mssql: { + text : 'CREATE INDEX [post_id_userId] ON [post] ([userId],[id] DESC)', + string: 'CREATE INDEX [post_id_userId] ON [post] ([userId],[id] DESC)' + }, + oracle: { + text : 'CREATE INDEX "post_id_userId" ON "post" ("userId","id" DESC)', + string: 'CREATE INDEX "post_id_userId" ON "post" ("userId","id" DESC)' + }, params: [] }); + Harness.test({ query: post.indexes().create(), pg: { @@ -118,22 +185,30 @@ Harness.test({ sqlite: { text : 'No columns defined!', throws: true + }, + oracle: { + text : 'No columns defined!', + throws: true } }); Harness.test({ query: post.indexes().drop('index_name'), pg: { - text : 'DROP INDEX "index_name" ON "post"', - string: 'DROP INDEX "index_name" ON "post"' + text : 'DROP INDEX "public"."index_name"', + string: 'DROP INDEX "public"."index_name"' }, mysql: { - text : 'DROP INDEX `index_name` ON `post`', - string: 'DROP INDEX `index_name` ON `post`' + text : 'DROP INDEX `public`.`index_name`', + string: 'DROP INDEX `public`.`index_name`' }, sqlite: { - text : 'DROP INDEX "index_name" ON "post"', - string: 'DROP INDEX "index_name" ON "post"' + text : 'DROP INDEX "public"."index_name"', + string: 'DROP INDEX "public"."index_name"' + }, + oracle: { + text : 'DROP INDEX "index_name"', + string: 'DROP INDEX "index_name"' }, params: [] }); @@ -141,16 +216,20 @@ Harness.test({ Harness.test({ query: post.indexes().drop(post.userId, post.id), pg: { - text : 'DROP INDEX "post_id_userId" ON "post"', - string: 'DROP INDEX "post_id_userId" ON "post"' + text : 'DROP INDEX "public"."post_id_userId"', + string: 'DROP INDEX "public"."post_id_userId"' }, mysql: { - text : 'DROP INDEX `post_id_userId` ON `post`', - string: 'DROP INDEX `post_id_userId` ON `post`' + text : 'DROP INDEX `public`.`post_id_userId`', + string: 'DROP INDEX `public`.`post_id_userId`' }, sqlite: { - text : 'DROP INDEX "post_id_userId" ON "post"', - string: 'DROP INDEX "post_id_userId" ON "post"' + text : 'DROP INDEX "public"."post_id_userId"', + string: 'DROP INDEX "public"."post_id_userId"' + }, + oracle: { + text : 'DROP INDEX "post_id_userId"', + string: 'DROP INDEX "post_id_userId"' }, params: [] }); diff --git a/test/dialects/insert-tests.js b/test/dialects/insert-tests.js index f9ef0efb..60ba6a62 100644 --- a/test/dialects/insert-tests.js +++ b/test/dialects/insert-tests.js @@ -3,6 +3,13 @@ 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)), @@ -18,6 +25,10 @@ Harness.test({ text : 'INSERT INTO `post` (`content`, `userId`) VALUES (?, ?)', string: 'INSERT INTO `post` (`content`, `userId`) VALUES (\'test\', 1)' }, + oracle: { + text : 'INSERT INTO "post" ("content", "userId") VALUES (:1, :2)', + string: 'INSERT INTO "post" ("content", "userId") VALUES (\'test\', 1)' + }, params: ['test', 1] }); @@ -35,9 +46,42 @@ Harness.test({ text : 'INSERT INTO `post` (`content`) VALUES (?)', string: 'INSERT INTO `post` (`content`) VALUES (\'whoah\')' }, + mssql: { + text : 'INSERT INTO [post] ([content]) VALUES (@1)', + string: 'INSERT INTO [post] ([content]) VALUES (\'whoah\')' + }, + oracle: { + text : 'INSERT INTO "post" ("content") VALUES (:1)', + string: 'INSERT INTO "post" ("content") VALUES (\'whoah\')' + }, params: ['whoah'] }); +Harness.test({ + query: post.insert({length: 0}), + pg: { + text : 'INSERT INTO "post" ("length") VALUES ($1)', + string: 'INSERT INTO "post" ("length") VALUES (0)' + }, + sqlite: { + text : 'INSERT INTO "post" ("length") VALUES ($1)', + string: 'INSERT INTO "post" ("length") VALUES (0)' + }, + mysql: { + text : 'INSERT INTO `post` (`length`) VALUES (?)', + string: 'INSERT INTO `post` (`length`) VALUES (0)' + }, + mssql: { + text : 'INSERT INTO [post] ([length]) VALUES (@1)', + string: 'INSERT INTO [post] ([length]) VALUES (0)' + }, + oracle: { + text : 'INSERT INTO "post" ("length") VALUES (:1)', + string: 'INSERT INTO "post" ("length") VALUES (0)' + }, + params: [0] +}); + Harness.test({ query: post.insert({ content: 'test', @@ -55,6 +99,10 @@ Harness.test({ text : 'INSERT INTO `post` (`content`, `userId`) VALUES (?, ?)', string: 'INSERT INTO `post` (`content`, `userId`) VALUES (\'test\', 2)' }, + oracle: { + text : 'INSERT INTO "post" ("content", "userId") VALUES (:1, :2)', + string: 'INSERT INTO "post" ("content", "userId") VALUES (\'test\', 2)' + }, params: ['test', 2] }); @@ -75,6 +123,10 @@ Harness.test({ 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] }); @@ -98,6 +150,10 @@ Harness.test({ text : 'INSERT INTO `post` (`content`) VALUES (?), (?)', string: 'INSERT INTO `post` (`content`) VALUES (\'whoah\'), (\'hey\')' }, + oracle: { + text : 'INSERT INTO "post" ("content") VALUES (:1), (:2)', + string: 'INSERT INTO "post" ("content") VALUES (\'whoah\'), (\'hey\')' + }, params: ['whoah', 'hey'] }); @@ -122,6 +178,10 @@ Harness.test({ text : 'INSERT INTO `post` (`content`, `userId`) VALUES (?, ?), (?, ?)', string: 'INSERT INTO `post` (`content`, `userId`) VALUES (\'whoah\', 1), (\'hey\', 2)' }, + oracle: { + text : 'INSERT INTO "post" ("content", "userId") VALUES (:1, :2), (:3, :4)', + string: 'INSERT INTO "post" ("content", "userId") VALUES (\'whoah\', 1), (\'hey\', 2)' + }, params: ['whoah', 1, 'hey', 2] }); @@ -151,6 +211,10 @@ Harness.test({ 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] }); @@ -172,6 +236,10 @@ Harness.test({ 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: [] }); @@ -190,6 +258,9 @@ Harness.test({ mssql: { throws: true }, + oracle: { + throws: true + }, params: [] }); @@ -208,6 +279,9 @@ Harness.test({ mssql: { throws: true }, + oracle: { + throws: true + }, params: [] }); @@ -226,6 +300,9 @@ Harness.test({ mssql: { throws: true }, + oracle: { + throws: true + }, params: [] }); @@ -244,6 +321,9 @@ Harness.test({ mssql: { throws: true }, + oracle: { + throws: true + }, params: [] }); @@ -262,6 +342,9 @@ Harness.test({ mssql: { throws: true }, + oracle: { + throws: true + }, params: [] }); @@ -292,7 +375,12 @@ Harness.test({ 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({ @@ -321,6 +409,11 @@ Harness.test({ 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'] } }); @@ -343,6 +436,10 @@ Harness.test({ 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%'] }); @@ -365,6 +462,10 @@ Harness.test({ 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%'] }); @@ -383,6 +484,10 @@ Harness.test({ 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%'] }); @@ -405,9 +510,38 @@ Harness.test({ 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)), @@ -427,6 +561,10 @@ Harness.test({ 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] }); @@ -451,6 +589,10 @@ Harness.test({ 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] }); @@ -478,9 +620,304 @@ Harness.test({ 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([]), @@ -490,3 +927,168 @@ Harness.test({ }, params: [] }); + +Harness.test({ + query: arrayTable.insert(arrayTable.id.value(1), arrayTable.numbers.value([2, 3, 4])), + pg: { + text : 'INSERT INTO "arraytest" ("id", "numbers") VALUES ($1, $2)', + string: 'INSERT INTO "arraytest" ("id", "numbers") VALUES (1, \'{2,3,4}\')' + }, + sqlite: { + text : 'INSERT INTO "arraytest" ("id", "numbers") VALUES ($1, $2)', + string: 'INSERT INTO "arraytest" ("id", "numbers") VALUES (1, \'[2,3,4]\')' + }, + mysql: { + text : 'INSERT INTO `arraytest` (`id`, `numbers`) VALUES (?, ?)', + string: 'INSERT INTO `arraytest` (`id`, `numbers`) VALUES (1, (2, 3, 4))' + }, + oracle: { + text : 'INSERT INTO "arraytest" ("id", "numbers") VALUES (:1, :2)', + string: 'INSERT INTO "arraytest" ("id", "numbers") VALUES (1, (2, 3, 4))' + } +}); + +Harness.test({ + query: arrayTable.insert(arrayTable.id.value(1), arrayTable.numbers.value(["one", "two", "three"])), + pg: { + text : 'INSERT INTO "arraytest" ("id", "numbers") VALUES ($1, $2)', + string: 'INSERT INTO "arraytest" ("id", "numbers") VALUES (1, \'{"one","two","three"}\')' + }, + sqlite: { + text : 'INSERT INTO "arraytest" ("id", "numbers") VALUES ($1, $2)', + string: 'INSERT INTO "arraytest" ("id", "numbers") VALUES (1, \'["one","two","three"]\')' + }, + mysql: { + text : 'INSERT INTO `arraytest` (`id`, `numbers`) VALUES (?, ?)', + string: 'INSERT INTO `arraytest` (`id`, `numbers`) VALUES (1, (\'one\', \'two\', \'three\'))' + }, + oracle: { + text : 'INSERT INTO "arraytest" ("id", "numbers") VALUES (:1, :2)', + string: 'INSERT INTO "arraytest" ("id", "numbers") VALUES (1, (\'one\', \'two\', \'three\'))' + } +}); + +Harness.test({ + query: post.insert(post.userId).select(user.id).from(user), + pg: { + text : 'INSERT INTO "post" ("userId") SELECT "user"."id" FROM "user"', + string: 'INSERT INTO "post" ("userId") SELECT "user"."id" FROM "user"' + }, + sqlite: { + text : 'INSERT INTO "post" ("userId") SELECT "user"."id" FROM "user"', + string: 'INSERT INTO "post" ("userId") SELECT "user"."id" FROM "user"' + }, + mysql: { + text : 'INSERT INTO `post` (`userId`) SELECT `user`.`id` FROM `user`', + string: 'INSERT INTO `post` (`userId`) SELECT `user`.`id` FROM `user`' + }, + mssql: { + text : 'INSERT INTO [post] ([userId]) SELECT [user].[id] FROM [user]', + string: 'INSERT INTO [post] ([userId]) SELECT [user].[id] FROM [user]' + }, + oracle: { + text : 'INSERT INTO "post" ("userId") SELECT "user"."id" FROM "user"', + string: 'INSERT INTO "post" ("userId") SELECT "user"."id" FROM "user"' + }, + params: [] +}); + +Harness.test({ + query: post.insert(post.userId).select(user.id).from(user).onConflict({ + columns: ['userId'], + update: ['content'] + }), + pg: { + text : 'INSERT INTO "post" ("userId") SELECT "user"."id" FROM "user" ON CONFLICT ("userId") DO UPDATE SET "content" = EXCLUDED."content"', + string: 'INSERT INTO "post" ("userId") SELECT "user"."id" FROM "user" ON CONFLICT ("userId") DO UPDATE SET "content" = EXCLUDED."content"' + }, + sqlite: { + throws: true + }, + mysql: { + throws: true + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: [] +}); + +Harness.test({ + query: post.insert(post.userId).add(user.select(user.id)), + pg: { + text : 'INSERT INTO "post" ("userId") SELECT "user"."id" FROM "user"', + string: 'INSERT INTO "post" ("userId") SELECT "user"."id" FROM "user"' + }, + sqlite: { + text : 'INSERT INTO "post" ("userId") SELECT "user"."id" FROM "user"', + string: 'INSERT INTO "post" ("userId") SELECT "user"."id" FROM "user"' + }, + mysql: { + text : 'INSERT INTO `post` (`userId`) SELECT `user`.`id` FROM `user`', + string: 'INSERT INTO `post` (`userId`) SELECT `user`.`id` FROM `user`' + }, + mssql: { + text : 'INSERT INTO [post] ([userId]) SELECT [user].[id] FROM [user]', + string: 'INSERT INTO [post] ([userId]) SELECT [user].[id] FROM [user]' + }, + oracle: { + text : 'INSERT INTO "post" ("userId") SELECT "user"."id" FROM "user"', + string: 'INSERT INTO "post" ("userId") SELECT "user"."id" FROM "user"' + }, + params: [] +}); + +Harness.test({ + query: post.insert(post.userId).add(user.select(user.id).from(user)), + pg: { + text : 'INSERT INTO "post" ("userId") SELECT "user"."id" FROM "user"', + string: 'INSERT INTO "post" ("userId") SELECT "user"."id" FROM "user"' + }, + sqlite: { + text : 'INSERT INTO "post" ("userId") SELECT "user"."id" FROM "user"', + string: 'INSERT INTO "post" ("userId") SELECT "user"."id" FROM "user"' + }, + mysql: { + text : 'INSERT INTO `post` (`userId`) SELECT `user`.`id` FROM `user`', + string: 'INSERT INTO `post` (`userId`) SELECT `user`.`id` FROM `user`' + }, + mssql: { + text : 'INSERT INTO [post] ([userId]) SELECT [user].[id] FROM [user]', + string: 'INSERT INTO [post] ([userId]) SELECT [user].[id] FROM [user]' + }, + oracle: { + text : 'INSERT INTO "post" ("userId") SELECT "user"."id" FROM "user"', + string: 'INSERT INTO "post" ("userId") SELECT "user"."id" FROM "user"' + }, + params: [] +}); + +Harness.test({ + query: post.insert(post.userId).add(user.select(user.id).order(user.id)), + pg: { + text : 'INSERT INTO "post" ("userId") SELECT "user"."id" FROM "user" ORDER BY "user"."id"', + string: 'INSERT INTO "post" ("userId") SELECT "user"."id" FROM "user" ORDER BY "user"."id"' + }, + sqlite: { + text : 'INSERT INTO "post" ("userId") SELECT "user"."id" FROM "user" ORDER BY "user"."id"', + string: 'INSERT INTO "post" ("userId") SELECT "user"."id" FROM "user" ORDER BY "user"."id"' + }, + mysql: { + text : 'INSERT INTO `post` (`userId`) SELECT `user`.`id` FROM `user` ORDER BY `user`.`id`', + string: 'INSERT INTO `post` (`userId`) SELECT `user`.`id` FROM `user` ORDER BY `user`.`id`' + }, + mssql: { + text : 'INSERT INTO [post] ([userId]) SELECT [user].[id] FROM [user] ORDER BY [user].[id]', + string: 'INSERT INTO [post] ([userId]) SELECT [user].[id] FROM [user] ORDER BY [user].[id]' + }, + oracle: { + text : 'INSERT INTO "post" ("userId") SELECT "user"."id" FROM "user" ORDER BY "user"."id"', + string: 'INSERT INTO "post" ("userId") SELECT "user"."id" FROM "user" ORDER BY "user"."id"' + }, + params: [] +}); + diff --git a/test/dialects/join-tests.js b/test/dialects/join-tests.js index 88792acf..2e0c6882 100644 --- a/test/dialects/join-tests.js +++ b/test/dialects/join-tests.js @@ -23,6 +23,10 @@ Harness.test({ text : 'SELECT [user].[name], [post].[content] FROM [user] INNER JOIN [post] ON ([user].[id] = [post].[userId])', string: 'SELECT [user].[name], [post].[content] FROM [user] INNER JOIN [post] ON ([user].[id] = [post].[userId])' }, + 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: [] }); @@ -44,6 +48,10 @@ Harness.test({ 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: [] }); @@ -70,6 +78,10 @@ Harness.test({ text : 'SELECT [user].[name], [post].[content], [comment].[text] FROM [user] INNER JOIN [post] ON ([user].[id] = [post].[userId]) INNER JOIN [comment] ON ([post].[id] = [comment].[postId])', string: 'SELECT [user].[name], [post].[content], [comment].[text] FROM [user] INNER JOIN [post] ON ([user].[id] = [post].[userId]) INNER JOIN [comment] ON ([post].[id] = [comment].[postId])' }, + 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: [] }); @@ -91,6 +103,10 @@ Harness.test({ text : 'SELECT [user].[name], [post].[content] FROM [user] LEFT JOIN [post] ON ([user].[id] = [post].[userId])', string: 'SELECT [user].[name], [post].[content] FROM [user] LEFT JOIN [post] ON ([user].[id] = [post].[userId])' }, + 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: [] }); @@ -117,6 +133,10 @@ Harness.test({ text : 'SELECT [user].[name], [post].[content] FROM [user] LEFT JOIN [post] ON ([user].[id] = [post].[userId]) LEFT JOIN [comment] ON ([post].[id] = [comment].[postId])', string: 'SELECT [user].[name], [post].[content] FROM [user] LEFT JOIN [post] ON ([user].[id] = [post].[userId]) LEFT JOIN [comment] ON ([post].[id] = [comment].[postId])' }, + 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: [] }); @@ -133,20 +153,78 @@ Harness.test({ .from(user.join(subposts) .on(user.id.equals(subposts.subpostUserId))), pg: { - text : 'SELECT "user"."name", "subposts"."content" FROM "user" INNER JOIN (SELECT "post"."content", "post"."userId" AS "subpostUserId" FROM "post") subposts ON ("user"."id" = "subposts"."subpostUserId")', - string: 'SELECT "user"."name", "subposts"."content" FROM "user" INNER JOIN (SELECT "post"."content", "post"."userId" AS "subpostUserId" FROM "post") subposts ON ("user"."id" = "subposts"."subpostUserId")' + text : 'SELECT "user"."name", "subposts"."content" FROM "user" INNER JOIN (SELECT "post"."content", "post"."userId" AS "subpostUserId" FROM "post") "subposts" ON ("user"."id" = "subposts"."subpostUserId")', + string: 'SELECT "user"."name", "subposts"."content" FROM "user" INNER JOIN (SELECT "post"."content", "post"."userId" AS "subpostUserId" FROM "post") "subposts" ON ("user"."id" = "subposts"."subpostUserId")' }, sqlite: { - text : 'SELECT "user"."name", "subposts"."content" FROM "user" INNER JOIN (SELECT "post"."content", "post"."userId" AS "subpostUserId" FROM "post") subposts ON ("user"."id" = "subposts"."subpostUserId")', - string: 'SELECT "user"."name", "subposts"."content" FROM "user" INNER JOIN (SELECT "post"."content", "post"."userId" AS "subpostUserId" FROM "post") subposts ON ("user"."id" = "subposts"."subpostUserId")' + text : 'SELECT "user"."name", "subposts"."content" FROM "user" INNER JOIN (SELECT "post"."content", "post"."userId" AS "subpostUserId" FROM "post") "subposts" ON ("user"."id" = "subposts"."subpostUserId")', + string: 'SELECT "user"."name", "subposts"."content" FROM "user" INNER JOIN (SELECT "post"."content", "post"."userId" AS "subpostUserId" FROM "post") "subposts" ON ("user"."id" = "subposts"."subpostUserId")' }, mysql: { - text : 'SELECT `user`.`name`, `subposts`.`content` FROM `user` INNER JOIN (SELECT `post`.`content`, `post`.`userId` AS `subpostUserId` FROM `post`) subposts ON (`user`.`id` = `subposts`.`subpostUserId`)', - string: 'SELECT `user`.`name`, `subposts`.`content` FROM `user` INNER JOIN (SELECT `post`.`content`, `post`.`userId` AS `subpostUserId` FROM `post`) subposts ON (`user`.`id` = `subposts`.`subpostUserId`)' + text : 'SELECT `user`.`name`, `subposts`.`content` FROM `user` INNER JOIN (SELECT `post`.`content`, `post`.`userId` AS `subpostUserId` FROM `post`) `subposts` ON (`user`.`id` = `subposts`.`subpostUserId`)', + string: 'SELECT `user`.`name`, `subposts`.`content` FROM `user` INNER JOIN (SELECT `post`.`content`, `post`.`userId` AS `subpostUserId` FROM `post`) `subposts` ON (`user`.`id` = `subposts`.`subpostUserId`)' + }, + mssql: { + text : 'SELECT [user].[name], [subposts].[content] FROM [user] INNER JOIN (SELECT [post].[content], [post].[userId] AS [subpostUserId] FROM [post]) [subposts] ON ([user].[id] = [subposts].[subpostUserId])', + string: 'SELECT [user].[name], [subposts].[content] FROM [user] INNER JOIN (SELECT [post].[content], [post].[userId] AS [subpostUserId] FROM [post]) [subposts] ON ([user].[id] = [subposts].[subpostUserId])' + }, + oracle: { + text : 'SELECT "user"."name", "subposts"."content" FROM "user" INNER JOIN (SELECT "post"."content", "post"."userId" "subpostUserId" FROM "post") "subposts" ON ("user"."id" = "subposts"."subpostUserId")', + string: 'SELECT "user"."name", "subposts"."content" FROM "user" INNER JOIN (SELECT "post"."content", "post"."userId" "subpostUserId" FROM "post") "subposts" ON ("user"."id" = "subposts"."subpostUserId")' + }, + params: [] +}); + +Harness.test({ + query: user.select().from(user.leftJoinLateral(post.subQuery().select(post.userId))), + pg: { + text : 'SELECT "user".* FROM "user" LEFT JOIN LATERAL (SELECT "post"."userId" FROM "post") ON true', + string: 'SELECT "user".* FROM "user" LEFT JOIN LATERAL (SELECT "post"."userId" FROM "post") ON true' + }, + mssql: { + text : 'SELECT [user].* FROM [user] OUTER APPLY (SELECT [post].[userId] FROM [post])', + string: 'SELECT [user].* FROM [user] OUTER APPLY (SELECT [post].[userId] FROM [post])' + }, + oracle: { + text : 'SELECT "user".* FROM "user" OUTER APPLY (SELECT "post"."userId" FROM "post")', + string: 'SELECT "user".* FROM "user" OUTER APPLY (SELECT "post"."userId" FROM "post")' + }, + params: [] +}); + +Harness.test({ + query: user.select().from(user.leftJoinLateral(post.subQuery().select(post.userId).where(user.id.equals(post.userId)))), + pg: { + text : 'SELECT "user".* FROM "user" LEFT JOIN LATERAL (SELECT "post"."userId" FROM "post" WHERE ("user"."id" = "post"."userId")) ON true', + string: 'SELECT "user".* FROM "user" LEFT JOIN LATERAL (SELECT "post"."userId" FROM "post" WHERE ("user"."id" = "post"."userId")) ON true' }, mssql: { - text : 'SELECT [user].[name], [subposts].[content] FROM [user] INNER JOIN (SELECT [post].[content], [post].[userId] AS [subpostUserId] FROM [post]) subposts ON ([user].[id] = [subposts].[subpostUserId])', - string: 'SELECT [user].[name], [subposts].[content] FROM [user] INNER JOIN (SELECT [post].[content], [post].[userId] AS [subpostUserId] FROM [post]) subposts ON ([user].[id] = [subposts].[subpostUserId])' + text : 'SELECT [user].* FROM [user] OUTER APPLY (SELECT [post].[userId] FROM [post] WHERE ([user].[id] = [post].[userId]))', + string: 'SELECT [user].* FROM [user] OUTER APPLY (SELECT [post].[userId] FROM [post] WHERE ([user].[id] = [post].[userId]))' + }, + oracle: { + text : 'SELECT "user".* FROM "user" OUTER APPLY (SELECT "post"."userId" FROM "post" WHERE ("user"."id" = "post"."userId"))', + string: 'SELECT "user".* FROM "user" OUTER APPLY (SELECT "post"."userId" FROM "post" WHERE ("user"."id" = "post"."userId"))' }, params: [] }); + +Harness.test({ + query: user.select().from(user + .leftJoinLateral(post.subQuery().select(post.userId)) + .leftJoinLateral(comment.subQuery().select(comment.postId))), + pg: { + text : 'SELECT "user".* FROM "user" LEFT JOIN LATERAL (SELECT "post"."userId" FROM "post") ON true LEFT JOIN LATERAL (SELECT "comment"."postId" FROM "comment") ON true', + string: 'SELECT "user".* FROM "user" LEFT JOIN LATERAL (SELECT "post"."userId" FROM "post") ON true LEFT JOIN LATERAL (SELECT "comment"."postId" FROM "comment") ON true' + }, + mssql: { + text : 'SELECT [user].* FROM [user] OUTER APPLY (SELECT [post].[userId] FROM [post]) OUTER APPLY (SELECT [comment].[postId] FROM [comment])', + string: 'SELECT [user].* FROM [user] OUTER APPLY (SELECT [post].[userId] FROM [post]) OUTER APPLY (SELECT [comment].[postId] FROM [comment])' + }, + oracle: { + text : 'SELECT "user".* FROM "user" OUTER APPLY (SELECT "post"."userId" FROM "post") OUTER APPLY (SELECT "comment"."postId" FROM "comment")', + string: 'SELECT "user".* FROM "user" OUTER APPLY (SELECT "post"."userId" FROM "post") OUTER APPLY (SELECT "comment"."postId" FROM "comment")' + }, + params: [] +}); + diff --git a/test/dialects/join-to-tests.js b/test/dialects/join-to-tests.js index e2d98cf0..d50f8817 100644 --- a/test/dialects/join-to-tests.js +++ b/test/dialects/join-to-tests.js @@ -54,6 +54,10 @@ Harness.test({ 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: [] }); @@ -75,6 +79,10 @@ Harness.test({ 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: [] }); @@ -96,5 +104,9 @@ Harness.test({ 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/limit-and-offset-tests.js b/test/dialects/limit-and-offset-tests.js index 39ffc7d6..6561c14b 100644 --- a/test/dialects/limit-and-offset-tests.js +++ b/test/dialects/limit-and-offset-tests.js @@ -66,6 +66,10 @@ Harness.test({ 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: [] }); @@ -91,6 +95,10 @@ Harness.test({ 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'] }); diff --git a/test/dialects/literal-tests.js b/test/dialects/literal-tests.js index e1940f5a..c31d4271 100644 --- a/test/dialects/literal-tests.js +++ b/test/dialects/literal-tests.js @@ -17,6 +17,10 @@ Harness.test({ 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: [] }); @@ -35,6 +39,10 @@ Harness.test({ 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: [] }); @@ -45,16 +53,20 @@ var subquery = user.subQuery('subquery_for_count').select(user.literal(1).as('co 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' + 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' + 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' + 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 index 4ce21406..c385641c 100644 --- a/test/dialects/matches-test.js +++ b/test/dialects/matches-test.js @@ -30,6 +30,10 @@ Harness.test({ 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'] }); diff --git a/test/dialects/namespace-tests.js b/test/dialects/namespace-tests.js index 9954c3f7..5f6b4a56 100644 --- a/test/dialects/namespace-tests.js +++ b/test/dialects/namespace-tests.js @@ -24,6 +24,10 @@ Harness.test({ 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: [] }); @@ -45,6 +49,10 @@ Harness.test({ 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: [] }); @@ -67,6 +75,10 @@ Harness.test({ 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] }); @@ -88,6 +100,10 @@ Harness.test({ text : 'SELECT [p].[content], [u].[name] FROM [user] AS [u] INNER JOIN [post] AS [p] ON (([u].[id] = [p].[userId]) AND ([p].[content] IS NOT NULL))', string: 'SELECT [p].[content], [u].[name] FROM [user] AS [u] INNER JOIN [post] AS [p] ON (([u].[id] = [p].[userId]) AND ([p].[content] IS NOT NULL))' }, + 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: [] }); @@ -122,5 +138,9 @@ Harness.test({ 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 index a16c8887..11647fee 100644 --- a/test/dialects/not-in-clause-tests.js +++ b/test/dialects/not-in-clause-tests.js @@ -21,6 +21,10 @@ Harness.test({ 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: [] }); @@ -42,6 +46,10 @@ Harness.test({ 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] }); @@ -63,6 +71,10 @@ Harness.test({ 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: [] }); @@ -84,6 +96,10 @@ Harness.test({ 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] }); @@ -105,6 +121,10 @@ Harness.test({ 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: [] }); @@ -126,6 +146,10 @@ Harness.test({ 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] }); @@ -147,5 +171,9 @@ Harness.test({ text : 'SELECT [post].* FROM [post] WHERE (NOT ([post].[id] IN (@1, @2) OR [post].[id] IS NULL))', string: 'SELECT [post].* FROM [post] WHERE (NOT ([post].[id] IN (1, 2) OR [post].[id] IS NULL))' }, + oracle: { + text : 'SELECT "post".* FROM "post" WHERE (NOT ("post"."id" IN (:1, :2) OR "post"."id" IS NULL))', + string: 'SELECT "post".* FROM "post" WHERE (NOT ("post"."id" IN (1, 2) OR "post"."id" IS NULL))' + }, params: [1, 2] }); diff --git a/test/dialects/order-tests.js b/test/dialects/order-tests.js index 107f0da4..a0813d8e 100644 --- a/test/dialects/order-tests.js +++ b/test/dialects/order-tests.js @@ -22,6 +22,10 @@ Harness.test({ 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: [] }); @@ -43,6 +47,10 @@ Harness.test({ text : 'SELECT [post].[content] FROM [post] ORDER BY [post].[content], [post].[userId] DESC', string: 'SELECT [post].[content] FROM [post] ORDER BY [post].[content], [post].[userId] DESC' }, + 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: [] }); @@ -64,6 +72,10 @@ Harness.test({ text : 'SELECT [post].[content] FROM [post] ORDER BY [post].[content], [post].[userId] DESC', string: 'SELECT [post].[content] FROM [post] ORDER BY [post].[content], [post].[userId] DESC' }, + oracle: { + text : 'SELECT "post"."content" FROM "post" ORDER BY "post"."content", "post"."userId" DESC', + string: 'SELECT "post"."content" FROM "post" ORDER BY "post"."content", "post"."userId" DESC' + }, params: [] }); @@ -85,6 +97,10 @@ Harness.test({ text : 'SELECT [post].[content] FROM [post] ORDER BY [post].[content], [post].[userId] DESC', string: 'SELECT [post].[content] FROM [post] ORDER BY [post].[content], [post].[userId] DESC' }, + 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: [] }); @@ -106,6 +122,10 @@ Harness.test({ text : 'SELECT [post].[content] FROM [post] ORDER BY [post].[content], [post].[userId] DESC', string: 'SELECT [post].[content] FROM [post] ORDER BY [post].[content], [post].[userId] DESC' }, + 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: [] }); @@ -127,6 +147,10 @@ Harness.test({ 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: [] }); @@ -148,6 +172,10 @@ Harness.test({ 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: [] }); @@ -169,6 +197,10 @@ Harness.test({ 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: [] }); @@ -190,6 +222,10 @@ Harness.test({ 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: [] }); @@ -211,5 +247,119 @@ Harness.test({ 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 index afddce99..40dde618 100644 --- a/test/dialects/regex-tests.js +++ b/test/dialects/regex-tests.js @@ -13,6 +13,15 @@ Harness.test({ 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: { @@ -21,3 +30,12 @@ Harness.test({ }, params: ['age'] }); + +Harness.test({ + query: customer.select(customer.metadata.notIregex('age')), + pg: { + text : 'SELECT ("customer"."metadata" !~* $1) FROM "customer"', + string: 'SELECT ("customer"."metadata" !~* \'age\') FROM "customer"' + }, + params: ['age'] +}); diff --git a/test/dialects/replace-tests.js b/test/dialects/replace-tests.js new file mode 100644 index 00000000..72af0e4b --- /dev/null +++ b/test/dialects/replace-tests.js @@ -0,0 +1,1000 @@ +'use strict'; + +var Harness = require('./support'); +var post = Harness.definePostTable(); +var user = Harness.defineUserTable(); +var contentTable = Harness.defineContentTable(); +var customerAliasTable = Harness.defineCustomerAliasTable(); + +var arrayTable = require('../../lib/table').define({ + name: 'arraytest', + columns: ['id', 'numbers'] +}); + +Harness.test({ + query: post.replace(post.content.value('test'), post.userId.value(1)), + pg: { + throws: true + }, + sqlite: { + text : 'REPLACE INTO "post" ("content", "userId") VALUES ($1, $2)', + string: 'REPLACE INTO "post" ("content", "userId") VALUES (\'test\', 1)' + }, + mysql: { + text : 'REPLACE INTO `post` (`content`, `userId`) VALUES (?, ?)', + string: 'REPLACE INTO `post` (`content`, `userId`) VALUES (\'test\', 1)' + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: ['test', 1] +}); + +Harness.test({ + query: post.replace(post.content.value('whoah')), + pg: { + throws: true + }, + sqlite: { + text : 'REPLACE INTO "post" ("content") VALUES ($1)', + string: 'REPLACE INTO "post" ("content") VALUES (\'whoah\')' + }, + mysql: { + text : 'REPLACE INTO `post` (`content`) VALUES (?)', + string: 'REPLACE INTO `post` (`content`) VALUES (\'whoah\')' + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: ['whoah'] +}); + +Harness.test({ + query: post.replace({length: 0}), + pg: { + throws: true + }, + sqlite: { + text : 'REPLACE INTO "post" ("length") VALUES ($1)', + string: 'REPLACE INTO "post" ("length") VALUES (0)' + }, + mysql: { + text : 'REPLACE INTO `post` (`length`) VALUES (?)', + string: 'REPLACE INTO `post` (`length`) VALUES (0)' + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: [0] +}); + +Harness.test({ + query: post.replace({ + content: 'test', + userId: 2 + }), + pg: { + throws: true + }, + sqlite: { + text : 'REPLACE INTO "post" ("content", "userId") VALUES ($1, $2)', + string: 'REPLACE INTO "post" ("content", "userId") VALUES (\'test\', 2)' + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: ['test', 2] +}); + +Harness.test({ + query: post.replace({ + content: post.sql.functions.LOWER('TEST'), + userId: 2 + }), + pg: { + throws: true + }, + sqlite: { + text : 'REPLACE INTO "post" ("content", "userId") VALUES (LOWER($1), $2)', + string: 'REPLACE INTO "post" ("content", "userId") VALUES (LOWER(\'TEST\'), 2)' + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: ['TEST', 2] +}); + +// allow bulk replace +Harness.test({ + query: post.replace([{ + content: 'whoah' + }, { + content: 'hey' + } + ]), + pg: { + throws: true + }, + sqlite: { + text : 'REPLACE INTO "post" ("content") VALUES ($1), ($2)', + string: 'REPLACE INTO "post" ("content") VALUES (\'whoah\'), (\'hey\')' + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: ['whoah', 'hey'] +}); + +Harness.test({ + query: post.replace([{ + content: 'whoah', + userId: 1 + }, { + content: 'hey', + userId: 2 + } + ]), + pg: { + throws: true + }, + sqlite: { + text : 'REPLACE INTO "post" ("content", "userId") VALUES ($1, $2), ($3, $4)', + string: 'REPLACE INTO "post" ("content", "userId") VALUES (\'whoah\', 1), (\'hey\', 2)' + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: ['whoah', 1, 'hey', 2] +}); + +// consistent order +Harness.test({ + query: post.replace([{ + content: 'whoah', + userId: 1 + }, { + userId: 2, + content: 'hey' + } + ]), + pg: { + throws: true + }, + sqlite: { + text : 'REPLACE INTO "post" ("content", "userId") VALUES ($1, $2), ($3, $4)', + string: 'REPLACE INTO "post" ("content", "userId") VALUES (\'whoah\', 1), (\'hey\', 2)' + }, + mysql: { + text : 'REPLACE INTO `post` (`content`, `userId`) VALUES (?, ?), (?, ?)', + string: 'REPLACE INTO `post` (`content`, `userId`) VALUES (\'whoah\', 1), (\'hey\', 2)' + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: ['whoah', 1, 'hey', 2] +}); + +Harness.test({ + query: post.replace({}), + pg: { + throws: true + }, + sqlite: { + text : 'REPLACE INTO "post" DEFAULT VALUES', + string: 'REPLACE INTO "post" DEFAULT VALUES' + }, + mysql: { + text : 'REPLACE INTO `post` () VALUES ()', + string: 'REPLACE INTO `post` () VALUES ()' + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: [] +}); + +Harness.test({ + query: post.replace({}).returning('*'), + pg: { + throws: true + }, + sqlite: { + throws: true + }, + mysql: { + throws: true + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: [] +}); + +Harness.test({ + query: post.replace({}).returning(post.star()), + pg: { + throws: true + }, + sqlite: { + throws: true + }, + mysql: { + throws: true + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: [] +}); + +Harness.test({ + query: post.replace({}).returning(post.id), + pg: { + throws: true + }, + sqlite: { + throws: true + }, + mysql: { + throws: true + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: [] +}); + +Harness.test({ + query: post.replace({}).returning(post.id, post.content), + pg: { + throws: true + }, + sqlite: { + throws: true + }, + mysql: { + throws: true + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: [] +}); + +Harness.test({ + query: post.replace({}).returning([post.id, post.content]), + pg: { + throws: true + }, + sqlite: { + throws: true + }, + mysql: { + throws: true + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: [] +}); + +// handle missing columns +Harness.test({ + query: post.replace([{ + content: 'whoah', + userId: 1 + }, { + content: 'hey' + } + ]), + pg: { + throws: true + }, + sqlite: { + text : 'Sqlite requires the same number of columns in each replace row', + throws: true + }, + mysql: { + text : 'REPLACE INTO `post` (`content`, `userId`) VALUES (?, ?), (?, DEFAULT)', + string: 'REPLACE INTO `post` (`content`, `userId`) VALUES (\'whoah\', 1), (\'hey\', DEFAULT)', + params: ['whoah', 1, 'hey'] + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, +}); + +Harness.test({ + query: post.replace([{ + userId: 1 + }, { + content: 'hey', + userId: 2 + } + ]), + pg: { + throws: true + }, + sqlite: { + text : 'Sqlite requires the same number of columns in each replace row', + throws: true + }, + mysql: { + text : 'REPLACE INTO `post` (`userId`, `content`) VALUES (?, DEFAULT), (?, ?)', + string: 'REPLACE INTO `post` (`userId`, `content`) VALUES (1, DEFAULT), (2, \'hey\')', + params: [1, 2, 'hey'] + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, +}); + +Harness.test({ + query: post.replace(post.content, post.userId) + .select('\'test\'', user.id).from(user).where(user.name.like('A%')), + pg: { + throws: true + }, + sqlite: { + text : 'REPLACE INTO "post" ("content", "userId") SELECT \'test\', "user"."id" FROM "user" WHERE ("user"."name" LIKE $1)', + string: 'REPLACE INTO "post" ("content", "userId") SELECT \'test\', "user"."id" FROM "user" WHERE ("user"."name" LIKE \'A%\')' + }, + mysql: { + text : 'REPLACE INTO `post` (`content`, `userId`) SELECT \'test\', `user`.`id` FROM `user` WHERE (`user`.`name` LIKE ?)', + string: 'REPLACE INTO `post` (`content`, `userId`) SELECT \'test\', `user`.`id` FROM `user` WHERE (`user`.`name` LIKE \'A%\')' + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: ['A%'] +}); + +Harness.test({ + query: post.replace([post.content, post.userId]) + .select('\'test\'', user.id).from(user).where(user.name.like('A%')), + pg: { + throws: true + }, + sqlite: { + text : 'REPLACE INTO "post" ("content", "userId") SELECT \'test\', "user"."id" FROM "user" WHERE ("user"."name" LIKE $1)', + string: 'REPLACE INTO "post" ("content", "userId") SELECT \'test\', "user"."id" FROM "user" WHERE ("user"."name" LIKE \'A%\')' + }, + mysql: { + text : 'REPLACE INTO `post` (`content`, `userId`) SELECT \'test\', `user`.`id` FROM `user` WHERE (`user`.`name` LIKE ?)', + string: 'REPLACE INTO `post` (`content`, `userId`) SELECT \'test\', `user`.`id` FROM `user` WHERE (`user`.`name` LIKE \'A%\')' + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: ['A%'] +}); + +Harness.test({ + query: post.replace(post.userId) + .select(user.id).from(user).where(user.name.like('A%')), + pg: { + throws: true + }, + sqlite: { + text : 'REPLACE INTO "post" ("userId") SELECT "user"."id" FROM "user" WHERE ("user"."name" LIKE $1)', + string: 'REPLACE INTO "post" ("userId") SELECT "user"."id" FROM "user" WHERE ("user"."name" LIKE \'A%\')' + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: ['A%'] +}); + +Harness.test({ + query: post.replace(post.userId) + .select(post.userId).from(user.join(post).on(user.id.equals(post.userId))).where(post.tags.like('A%')), + pg: { + throws: true + }, + sqlite: { + text : 'REPLACE INTO "post" ("userId") SELECT "post"."userId" FROM "user" INNER JOIN "post" ON ("user"."id" = "post"."userId") WHERE ("post"."tags" LIKE $1)', + string: 'REPLACE INTO "post" ("userId") SELECT "post"."userId" FROM "user" INNER JOIN "post" ON ("user"."id" = "post"."userId") WHERE ("post"."tags" LIKE \'A%\')' + }, + mysql: { + text : 'REPLACE INTO `post` (`userId`) SELECT `post`.`userId` FROM `user` INNER JOIN `post` ON (`user`.`id` = `post`.`userId`) WHERE (`post`.`tags` LIKE ?)', + string: 'REPLACE INTO `post` (`userId`) SELECT `post`.`userId` FROM `user` INNER JOIN `post` ON (`user`.`id` = `post`.`userId`) WHERE (`post`.`tags` LIKE \'A%\')' + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: ['A%'] +}); + +Harness.test({ + query: post.replace(post.userId).select(user.id).distinct().from(user), + pg: { + throws: true + }, + sqlite: { + text : 'REPLACE INTO "post" ("userId") SELECT DISTINCT "user"."id" FROM "user"', + string: 'REPLACE INTO "post" ("userId") SELECT DISTINCT "user"."id" FROM "user"' + }, + mysql: { + text : 'REPLACE INTO `post` (`userId`) SELECT DISTINCT `user`.`id` FROM `user`', + string: 'REPLACE INTO `post` (`userId`) SELECT DISTINCT `user`.`id` FROM `user`' + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: [] +}); + +// Binary replaces +Harness.test({ + query: post.replace(post.content.value(new Buffer('test')), post.userId.value(2)), + pg: { + throws: true + }, + sqlite: { + text : 'REPLACE INTO "post" ("content", "userId") VALUES ($1, $2)', + string: 'REPLACE INTO "post" ("content", "userId") VALUES (x\'74657374\', 2)' + }, + mysql: { + text : 'REPLACE INTO `post` (`content`, `userId`) VALUES (?, ?)', + string: 'REPLACE INTO `post` (`content`, `userId`) VALUES (x\'74657374\', 2)' + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: [new Buffer('test'), 2] +}); + +Harness.test({ + query: post.replace({ + content: new Buffer('test'), + userId: 2 + }), + pg: { + throws: true + }, + sqlite: { + text : 'REPLACE INTO "post" ("content", "userId") VALUES ($1, $2)', + string: 'REPLACE INTO "post" ("content", "userId") VALUES (x\'74657374\', 2)' + }, + mysql: { + text : 'REPLACE INTO `post` (`content`, `userId`) VALUES (?, ?)', + string: 'REPLACE INTO `post` (`content`, `userId`) VALUES (x\'74657374\', 2)' + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: [new Buffer('test'), 2] +}); + +Harness.test({ + query: post.replace([{ + content: new Buffer('whoah') + }, { + content: new Buffer('hey') + } + ]), + pg: { + throws: true + }, + sqlite: { + text : 'REPLACE INTO "post" ("content") VALUES ($1), ($2)', + string: 'REPLACE INTO "post" ("content") VALUES (x\'77686f6168\'), (x\'686579\')' + }, + mysql: { + text : 'REPLACE INTO `post` (`content`) VALUES (?), (?)', + string: 'REPLACE INTO `post` (`content`) VALUES (x\'77686f6168\'), (x\'686579\')' + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: [new Buffer('whoah'), new Buffer('hey')] +}); + +Harness.test({ + query: post.replace({ + content: 'test', + userId: 2 + }).onDuplicate({ + content: 'testupdate', + }), + pg: { + throws: true + }, + sqlite: { + throws: true + }, + mysql: { + text : 'REPLACE INTO `post` (`content`, `userId`) VALUES (?, ?) ON DUPLICATE KEY UPDATE `post`.`content` = ?', + string: 'REPLACE INTO `post` (`content`, `userId`) VALUES (\'test\', 2) ON DUPLICATE KEY UPDATE `post`.`content` = \'testupdate\'' + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: ['test', 2, 'testupdate'] +}); + +Harness.test({ + query: customerAliasTable.replace({ + id : 2, + name : 'test' + }).onConflict({ + columns: ['id'], + update: ['name'] + }), + mysql: { + throws: true + }, + sqlite: { + throws: true + }, + pg: { + throws: true + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: [2, 'test'] +}); + +Harness.test({ + query: customerAliasTable.replace({ + id : 2, + name : 'test' + }).orIgnore(), + mysql: { + throws: true + }, + sqlite: { + text : 'REPLACE OR IGNORE INTO "customer" ("id", "name") VALUES ($1, $2)', + string: 'REPLACE OR IGNORE INTO "customer" ("id", "name") VALUES (2, \'test\')' + }, + pg: { + throws: true + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: [2, 'test'] +}); + +Harness.test({ + query: post.replace({ + content: 'test', + userId: 2 + }).onConflict({ + columns: ['userId'], + update: ['content'] + }), + mysql: { + throws: true + }, + sqlite: { + throws: true + }, + pg: { + throws: true + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: ['test', 2] +}); + +Harness.test({ + query: post.replace({ + content: 'test', + userId: 2 + }).onConflict({ + columns: ['userId','content'], + update: ['content','userId'] + }), + mysql: { + throws: true + }, + sqlite: { + throws: true + }, + pg: { + throws: true + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: ['test', 2] +}); + +Harness.test({ + query: post.replace({ + content: 'test', + userId: 2 + }).onConflict({ + columns: ['userId'], + update: ['content'] + }).where(post.userId.equals(2)), + mysql: { + throws: true + }, + sqlite: { + throws: true + }, + pg: { + throws: true + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: ['test', 2, 2] +}); + +Harness.test({ + query: post.replace({ + content: 'test', + userId: 2 + }).onConflict({ + constraint: 'conc_userId', + update: ['content'] + }).where(post.userId.equals(2)), + mysql: { + throws: true + }, + sqlite: { + throws: true + }, + pg: { + throws: true + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: ['test', 2, 2] +}); + +Harness.test({ + query: post.replace({ + content: 'test', + userId: 2 + }).onConflict({ + columns: ['userId'], + }), + mysql: { + throws: true + }, + sqlite: { + throws: true + }, + pg: { + throws: true + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: ['test', 2] +}); + +Harness.test({ + query: post.replace({ + content: 'test', + userId: 2 + }).onConflict({ + constraint: 'conc_userId', + }), + mysql: { + throws: true + }, + sqlite: { + throws: true + }, + pg: { + throws: true + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: ['test', 2] +}); + +Harness.test({ + query: contentTable.replace({ + contentId: 20, + text : "something" + }).onConflict({ + columns: ['contentId'], + }), + mysql: { + throws: true + }, + sqlite: { + throws: true + }, + pg: { + throws: true + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: [20, "something"] +}); + +Harness.test({ + query: contentTable.replace({ + contentId: 20, + text : "something", + contentPosts : "another thing", + }).onConflict({ + columns: ['contentId'], + update: ['contentPosts'] + }), + mysql: { + throws: true + }, + sqlite: { + throws: true + }, + pg: { + throws: true + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: [20, "something", "another thing"] +}); + +Harness.test({ + query: post.replace([]), + + mysql: { + text : 'SELECT `post`.* FROM `post` WHERE (1=2)', + string: 'SELECT `post`.* FROM `post` WHERE (1=2)' + }, + params: [] +}); + +Harness.test({ + query: arrayTable.replace(arrayTable.id.value(1), arrayTable.numbers.value([2, 3, 4])), + pg: { + throws: true + }, + sqlite: { + text : 'REPLACE INTO "arraytest" ("id", "numbers") VALUES ($1, $2)', + string: 'REPLACE INTO "arraytest" ("id", "numbers") VALUES (1, \'[2,3,4]\')' + }, + mssql: { + throws: true + }, + oracle: { + throws: true + } +}); + +Harness.test({ + query: arrayTable.replace(arrayTable.id.value(1), arrayTable.numbers.value(["one", "two", "three"])), + pg: { + throws: true + }, + sqlite: { + text : 'REPLACE INTO "arraytest" ("id", "numbers") VALUES ($1, $2)', + string: 'REPLACE INTO "arraytest" ("id", "numbers") VALUES (1, \'["one","two","three"]\')' + }, + mssql: { + throws: true + }, + oracle: { + throws: true + } +}); + +Harness.test({ + query: post.replace(post.userId).select(user.id).from(user), + pg: { + throws: true + }, + sqlite: { + text : 'REPLACE INTO "post" ("userId") SELECT "user"."id" FROM "user"', + string: 'REPLACE INTO "post" ("userId") SELECT "user"."id" FROM "user"' + }, + mysql: { + text : 'REPLACE INTO `post` (`userId`) SELECT `user`.`id` FROM `user`', + string: 'REPLACE INTO `post` (`userId`) SELECT `user`.`id` FROM `user`' + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: [] +}); + +Harness.test({ + query: post.replace(post.userId).select(user.id).from(user).onConflict({ + columns: ['userId'], + update: ['content'] + }), + pg: { + throws: true + }, + sqlite: { + throws: true + }, + mysql: { + throws: true + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: [] +}); + +Harness.test({ + query: post.replace(post.userId).add(user.select(user.id)), + pg: { + throws: true + }, + sqlite: { + text : 'REPLACE INTO "post" ("userId") SELECT "user"."id" FROM "user"', + string: 'REPLACE INTO "post" ("userId") SELECT "user"."id" FROM "user"' + }, + mysql: { + text : 'REPLACE INTO `post` (`userId`) SELECT `user`.`id` FROM `user`', + string: 'REPLACE INTO `post` (`userId`) SELECT `user`.`id` FROM `user`' + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: [] +}); + +Harness.test({ + query: post.replace(post.userId).add(user.select(user.id).from(user)), + pg: { + throws: true + }, + sqlite: { + text : 'REPLACE INTO "post" ("userId") SELECT "user"."id" FROM "user"', + string: 'REPLACE INTO "post" ("userId") SELECT "user"."id" FROM "user"' + }, + mysql: { + text : 'REPLACE INTO `post` (`userId`) SELECT `user`.`id` FROM `user`', + string: 'REPLACE INTO `post` (`userId`) SELECT `user`.`id` FROM `user`' + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: [] +}); + +Harness.test({ + query: post.replace(post.userId).add(user.select(user.id).order(user.id)), + pg: { + throws: true + }, + sqlite: { + text : 'REPLACE INTO "post" ("userId") SELECT "user"."id" FROM "user" ORDER BY "user"."id"', + string: 'REPLACE INTO "post" ("userId") SELECT "user"."id" FROM "user" ORDER BY "user"."id"' + }, + mysql: { + text : 'REPLACE INTO `post` (`userId`) SELECT `user`.`id` FROM `user` ORDER BY `user`.`id`', + string: 'REPLACE INTO `post` (`userId`) SELECT `user`.`id` FROM `user` ORDER BY `user`.`id`' + }, + mssql: { + throws: true + }, + oracle: { + throws: true + }, + params: [] +}); diff --git a/test/dialects/returning-tests.js b/test/dialects/returning-tests.js new file mode 100644 index 00000000..ebbb0b2e --- /dev/null +++ b/test/dialects/returning-tests.js @@ -0,0 +1,20 @@ +var Harness = require('./support'); +var user = Harness.defineUserTable(); + +Harness.test({ + query: user.insert({name: 'joe'}).returning(), + pg: { + text : 'INSERT INTO "user" ("name") VALUES ($1) RETURNING *', + string: 'INSERT INTO "user" ("name") VALUES (\'joe\') RETURNING *' + }, + params: ['joe'] +}); + +Harness.test({ + query: user.insert({name: 'joe'}).returning('id'), + pg: { + text : 'INSERT INTO "user" ("name") VALUES ($1) RETURNING id', + string: 'INSERT INTO "user" ("name") VALUES (\'joe\') RETURNING id' + }, + params: ['joe'] +}); diff --git a/test/dialects/schema-tests.js b/test/dialects/schema-tests.js index 417037f5..11e330da 100644 --- a/test/dialects/schema-tests.js +++ b/test/dialects/schema-tests.js @@ -28,6 +28,10 @@ Harness.test({ 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: [] }); @@ -49,6 +53,10 @@ Harness.test({ 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: [] }); @@ -70,6 +78,10 @@ Harness.test({ 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: [] }); @@ -92,6 +104,10 @@ Harness.test({ 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: [] }); @@ -119,6 +135,10 @@ Harness.test({ text : 'SELECT [staging].[user].[name], [dev].[post].[content] FROM [staging].[user] INNER JOIN [dev].[post] ON ([staging].[user].[id] = [dev].[post].[userId])', string: 'SELECT [staging].[user].[name], [dev].[post].[content] FROM [staging].[user] INNER JOIN [dev].[post] ON ([staging].[user].[id] = [dev].[post].[userId])' }, + 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: [] }); @@ -140,5 +160,9 @@ Harness.test({ text : 'SELECT [uws].[name], [dev].[post].[content] FROM [staging].[user] AS [uws] INNER JOIN [dev].[post] ON ([uws].[id] = [dev].[post].[userId])', string: 'SELECT [uws].[name], [dev].[post].[content] FROM [staging].[user] AS [uws] INNER JOIN [dev].[post] ON ([uws].[id] = [dev].[post].[userId])' }, + oracle: { + text : 'SELECT "uws"."name", "dev"."post"."content" FROM "staging"."user" "uws" INNER JOIN "dev"."post" ON ("uws"."id" = "dev"."post"."userId")', + string: 'SELECT "uws"."name", "dev"."post"."content" FROM "staging"."user" "uws" INNER JOIN "dev"."post" ON ("uws"."id" = "dev"."post"."userId")' + }, params: [] }); diff --git a/test/dialects/select-tests.js b/test/dialects/select-tests.js index 37ba87de..2892c8ae 100644 --- a/test/dialects/select-tests.js +++ b/test/dialects/select-tests.js @@ -2,7 +2,9 @@ var Harness = require('./support'); var post = Harness.definePostTable(); +var user = Harness.defineUserTable(); var customerAlias = Harness.defineCustomerAliasTable(); +var Sql = require('../../lib'); Harness.test({ query: post.select(post.id).select(post.content), @@ -22,6 +24,10 @@ Harness.test({ 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: [] }); @@ -39,5 +45,417 @@ Harness.test({ 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: [] -}); \ No newline at end of file +}); + + diff --git a/test/dialects/shortcut-tests.js b/test/dialects/shortcut-tests.js index 1c6efc2c..35c9a90b 100644 --- a/test/dialects/shortcut-tests.js +++ b/test/dialects/shortcut-tests.js @@ -23,6 +23,10 @@ Harness.test({ text : 'SELECT [user].* FROM [user]', string: 'SELECT [user].* FROM [user]' }, + oracle: { + text : 'SELECT "user".* FROM "user"', + string: 'SELECT "user".* FROM "user"' + }, params: [] }); @@ -44,6 +48,10 @@ Harness.test({ 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] }); @@ -65,6 +73,10 @@ Harness.test({ 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] }); @@ -87,6 +99,10 @@ Harness.test({ 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: [] }); @@ -108,6 +124,10 @@ Harness.test({ 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] }); @@ -133,5 +153,9 @@ Harness.test({ text : 'SELECT * FROM [post] WHERE ((([post].[content] IS NULL) OR ([post].[content] = @1)) AND ([post].[userId] = @2))', string: 'SELECT * FROM [post] WHERE ((([post].[content] IS NULL) OR ([post].[content] = \'\')) AND ([post].[userId] = 1))' }, + oracle: { + text : 'SELECT * FROM "post" WHERE ((("post"."content" IS NULL) OR ("post"."content" = :1)) AND ("post"."userId" = :2))', + string: 'SELECT * FROM "post" WHERE ((("post"."content" IS NULL) OR ("post"."content" = \'\')) AND ("post"."userId" = 1))' + }, params: ['', 1] }); diff --git a/test/dialects/subfield-tests.js b/test/dialects/subfield-tests.js new file mode 100644 index 00000000..aeaab51d --- /dev/null +++ b/test/dialects/subfield-tests.js @@ -0,0 +1,42 @@ +'use strict'; + +var Harness = require('./support'); +var customer = Harness.defineCustomerCompositeTable(); +var Sql = require('../../lib').setDialect('postgres'); + +Harness.test({ + query: customer.select(customer.info.subfields.age), + pg: { + text : 'SELECT ("customer"."info")."age" FROM "customer"', + string: 'SELECT ("customer"."info")."age" FROM "customer"' + }, + params: [] +}); + + +Harness.test({ + query: customer.select(customer.info.subfields.age.as('years')), + pg: { + text : 'SELECT ("customer"."info")."age" AS "years" FROM "customer"', + string: 'SELECT ("customer"."info")."age" AS "years" FROM "customer"' + }, + params: [] +}); + +Harness.test({ + query: customer.select(customer.id).where(customer.info.subfields.salary.equals(10)), + pg: { + text : 'SELECT "customer"."id" FROM "customer" WHERE (("customer"."info")."salary" = $1)', + string: 'SELECT "customer"."id" FROM "customer" WHERE (("customer"."info")."salary" = 10)' + }, + params: [10] +}); + +Harness.test({ + query: customer.select(customer.info.subfields.name.distinct()), + pg: { + text : 'SELECT DISTINCT(("customer"."info")."name") FROM "customer"', + string: 'SELECT DISTINCT(("customer"."info")."name") FROM "customer"' + }, + params: [] +}); diff --git a/test/dialects/subquery-tests.js b/test/dialects/subquery-tests.js index 8a5aa16e..05d76f1f 100644 --- a/test/dialects/subquery-tests.js +++ b/test/dialects/subquery-tests.js @@ -3,8 +3,34 @@ 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( @@ -27,6 +53,10 @@ Harness.test({ 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%'] }); @@ -48,26 +78,64 @@ Harness.test({ text : 'SELECT * FROM (SELECT * FROM [user])', string: 'SELECT * FROM (SELECT * FROM [user])' }, + oracle: { + text : 'SELECT * FROM (SELECT * FROM "user")', + string: 'SELECT * FROM (SELECT * FROM "user")' + }, params: [] }); +// Subquery with a date +Harness.test({ + query: Sql.select('*').from(post.subQuery().where(post.content.equals(new Date('Sat, 01 Jan 2000 00:00:00 GMT')))), + pg: { + text : 'SELECT * FROM (SELECT * FROM "post" WHERE ("post"."content" = $1))', + string: 'SELECT * FROM (SELECT * FROM "post" WHERE ("post"."content" = \'2000-01-01T00:00:00.000Z\'))' + }, + sqlite: { + text : 'SELECT * FROM (SELECT * FROM "post" WHERE ("post"."content" = $1))', + string: 'SELECT * FROM (SELECT * FROM "post" WHERE ("post"."content" = 946684800000))', + config: { + dateTimeMillis: true + } + }, + mysql: { + text : 'SELECT * FROM (SELECT * FROM `post` WHERE (`post`.`content` = ?))', + string: 'SELECT * FROM (SELECT * FROM `post` WHERE (`post`.`content` = \'2000-01-01T00:00:00.000Z\'))' + }, + mssql: { + text : 'SELECT * FROM (SELECT * FROM [post] WHERE ([post].[content] = @1))', + string: 'SELECT * FROM (SELECT * FROM [post] WHERE ([post].[content] = \'2000-01-01T00:00:00.000Z\'))' + }, + oracle: { + text : 'SELECT * FROM (SELECT * FROM "post" WHERE ("post"."content" = :1))', + string: 'SELECT * FROM (SELECT * FROM "post" WHERE ("post"."content" = \'2000-01-01T00:00:00.000Z\'))' + }, + params: [new Date('Sat, 01 Jan 2000 00:00:00 GMT')] +}); + + Harness.test({ query: Sql.select('*').from(customer.subQuery('T1')).from(user.subQuery('T2')), pg: { - text : 'SELECT * FROM (SELECT * FROM "customer") T1 , (SELECT * FROM "user") T2', - string: 'SELECT * FROM (SELECT * FROM "customer") T1 , (SELECT * FROM "user") T2' + text : 'SELECT * FROM (SELECT * FROM "customer") "T1" , (SELECT * FROM "user") "T2"', + string: 'SELECT * FROM (SELECT * FROM "customer") "T1" , (SELECT * FROM "user") "T2"' }, sqlite: { - text : 'SELECT * FROM (SELECT * FROM "customer") T1 , (SELECT * FROM "user") T2', - string: 'SELECT * FROM (SELECT * FROM "customer") T1 , (SELECT * FROM "user") T2' + text : 'SELECT * FROM (SELECT * FROM "customer") "T1" , (SELECT * FROM "user") "T2"', + string: 'SELECT * FROM (SELECT * FROM "customer") "T1" , (SELECT * FROM "user") "T2"' }, mysql: { - text : 'SELECT * FROM (SELECT * FROM `customer`) T1 , (SELECT * FROM `user`) T2', - string: 'SELECT * FROM (SELECT * FROM `customer`) T1 , (SELECT * FROM `user`) T2' + text : 'SELECT * FROM (SELECT * FROM `customer`) `T1` , (SELECT * FROM `user`) `T2`', + string: 'SELECT * FROM (SELECT * FROM `customer`) `T1` , (SELECT * FROM `user`) `T2`' }, mssql: { - text : 'SELECT * FROM (SELECT * FROM [customer]) T1 , (SELECT * FROM [user]) T2', - string: 'SELECT * FROM (SELECT * FROM [customer]) T1 , (SELECT * FROM [user]) T2' + 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: [] }); @@ -93,6 +161,10 @@ Harness.test({ text : '([customer].[name] BETWEEN (SELECT MIN([customer].[name]) FROM [customer]) AND (SELECT MAX([customer].[name]) FROM [customer]))', string: '([customer].[name] BETWEEN (SELECT MIN([customer].[name]) FROM [customer]) AND (SELECT MAX([customer].[name]) FROM [customer]))' }, + 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: [] }); @@ -114,5 +186,88 @@ Harness.test({ text : '(EXISTS (SELECT * FROM [user] WHERE ([user].[name] = [customer].[name])))', string: '(EXISTS (SELECT * FROM [user] WHERE ([user].[name] = [customer].[name])))' }, + oracle: { + text : '(EXISTS (SELECT * FROM "user" WHERE ("user"."name" = "customer"."name")))', + string: '(EXISTS (SELECT * FROM "user" WHERE ("user"."name" = "customer"."name")))' + }, + params: [] +}); + +var limitUsers = user.subQuery('limit-users').select(user.id, user.name).from(user).order(user.name).limit(10).offset(10); +Harness.test({ + query: Sql.select(limitUsers.name, post.tags).from(limitUsers.leftJoin(post).on(post.userId.equals(limitUsers.id))), + pg: { + text : 'SELECT "limit-users"."name", "post"."tags" FROM (SELECT "user"."id", "user"."name" FROM "user" ORDER BY "user"."name" LIMIT 10 OFFSET 10) "limit-users" LEFT JOIN "post" ON ("post"."userId" = "limit-users"."id")', + string: 'SELECT "limit-users"."name", "post"."tags" FROM (SELECT "user"."id", "user"."name" FROM "user" ORDER BY "user"."name" LIMIT 10 OFFSET 10) "limit-users" LEFT JOIN "post" ON ("post"."userId" = "limit-users"."id")' + }, + sqlite: { + text : 'SELECT "limit-users"."name", "post"."tags" FROM (SELECT "user"."id", "user"."name" FROM "user" ORDER BY "user"."name" LIMIT 10 OFFSET 10) "limit-users" LEFT JOIN "post" ON ("post"."userId" = "limit-users"."id")', + string: 'SELECT "limit-users"."name", "post"."tags" FROM (SELECT "user"."id", "user"."name" FROM "user" ORDER BY "user"."name" LIMIT 10 OFFSET 10) "limit-users" LEFT JOIN "post" ON ("post"."userId" = "limit-users"."id")' + }, + mysql: { + text : 'SELECT `limit-users`.`name`, `post`.`tags` FROM (SELECT `user`.`id`, `user`.`name` FROM `user` ORDER BY `user`.`name` LIMIT 10 OFFSET 10) `limit-users` LEFT JOIN `post` ON (`post`.`userId` = `limit-users`.`id`)', + string: 'SELECT `limit-users`.`name`, `post`.`tags` FROM (SELECT `user`.`id`, `user`.`name` FROM `user` ORDER BY `user`.`name` LIMIT 10 OFFSET 10) `limit-users` LEFT JOIN `post` ON (`post`.`userId` = `limit-users`.`id`)' + }, + mssql: { + text : 'SELECT [limit-users].[name], [post].[tags] FROM (SELECT [user].[id], [user].[name] FROM [user] ORDER BY [user].[name] OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY) [limit-users] LEFT JOIN [post] ON ([post].[userId] = [limit-users].[id])', + string: 'SELECT [limit-users].[name], [post].[tags] FROM (SELECT [user].[id], [user].[name] FROM [user] ORDER BY [user].[name] OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY) [limit-users] LEFT JOIN [post] ON ([post].[userId] = [limit-users].[id])' + }, + oracle: { + text : 'SELECT "limit-users"."name", "post"."tags" FROM (SELECT "user"."id", "user"."name" FROM "user" ORDER BY "user"."name" OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY) "limit-users" LEFT JOIN "post" ON ("post"."userId" = "limit-users"."id")', + string: 'SELECT "limit-users"."name", "post"."tags" FROM (SELECT "user"."id", "user"."name" FROM "user" ORDER BY "user"."name" OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY) "limit-users" LEFT JOIN "post" ON ("post"."userId" = "limit-users"."id")' + }, params: [] }); + +// Top-level subQuery +Harness.test({ + query: Sql.subQuery().select(user.id).from(user), + pg: { + text : '(SELECT "user"."id" FROM "user")', + string: '(SELECT "user"."id" FROM "user")' + }, + sqlite: { + text : '(SELECT "user"."id" FROM "user")', + string: '(SELECT "user"."id" FROM "user")' + }, + mysql: { + text : '(SELECT `user`.`id` FROM `user`)', + string: '(SELECT `user`.`id` FROM `user`)' + }, + mssql: { + text : '(SELECT [user].[id] FROM [user])', + string: '(SELECT [user].[id] FROM [user])' + }, + oracle: { + text : '(SELECT "user"."id" FROM "user")', + string: '(SELECT "user"."id" FROM "user")' + }, + params: [] +}); + +// Top-level subQuery with alias +Harness.test({ + query: Sql.subQuery("x").select(user.id).from(user), + pg: { + text : '(SELECT "user"."id" FROM "user") "x"', + string: '(SELECT "user"."id" FROM "user") "x"' + }, + sqlite: { + text : '(SELECT "user"."id" FROM "user") "x"', + string: '(SELECT "user"."id" FROM "user") "x"' + }, + mysql: { + text : '(SELECT `user`.`id` FROM `user`) `x`', + string: '(SELECT `user`.`id` FROM `user`) `x`' + }, + mssql: { + text : '(SELECT [user].[id] FROM [user]) [x]', + string: '(SELECT [user].[id] FROM [user]) [x]' + }, + oracle: { + text : '(SELECT "user"."id" FROM "user") "x"', + string: '(SELECT "user"."id" FROM "user") "x"' + }, + params: [] +}); + diff --git a/test/dialects/support.js b/test/dialects/support.js index e985a2da..4a9fa427 100644 --- a/test/dialects/support.js +++ b/test/dialects/support.js @@ -8,7 +8,8 @@ var dialects = { pg : require('../../lib/dialect/postgres'), sqlite : require('../../lib/dialect/sqlite'), mysql : require('../../lib/dialect/mysql'), - mssql : require('../../lib/dialect/mssql') + mssql : require('../../lib/dialect/mssql'), + oracle : require('../../lib/dialect/oracle') }; module.exports = { @@ -26,11 +27,11 @@ module.exports = { // check if this query is expected to throw if (expectedObject.throws) { assert.throws(function() { - new DialectClass().getQuery(expected.query); + new DialectClass(expectedObject.config).getQuery(expected.query); }); } else { // build query for dialect - var compiledQuery = new DialectClass().getQuery(expected.query); + var compiledQuery = new DialectClass(expectedObject.config).getQuery(expected.query); // test result is correct var expectedText = expectedObject.text || expectedObject; @@ -50,10 +51,10 @@ module.exports = { // test the toString if (expectedObject.throws) { assert.throws(function() { - new DialectClass().getString(expected.query); + new DialectClass(expectedObject.config).getString(expected.query); }); } else { - var compiledString = new DialectClass().getString(expected.query); + var compiledString = new DialectClass(expectedObject.config).getString(expected.query); // test result is correct assert.equal(compiledString, expectedObject.string); @@ -75,7 +76,7 @@ module.exports = { definePostTable: function() { return Table.define({ name: 'post', - columns: ['id', 'userId', 'content', 'tags'] + columns: ['id', 'userId', 'content', 'tags', 'length'] }); }, @@ -93,6 +94,17 @@ module.exports = { }); }, + // 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', @@ -112,5 +124,14 @@ module.exports = { name: 'variable', columns: ['a', 'b', 'c', 'd', 't', 'u', 'v', 'x', 'y', 'z'] }); + }, + +// this table is for testing snakeName related stuff + defineContentTable: function() { + return Table.define({ + name: 'content', + columns: ['content_id', 'text', 'content_posts'], + snakeToCamel: true + }); } }; diff --git a/test/dialects/table-tests.js b/test/dialects/table-tests.js index 26c5d26d..6740a7c8 100644 --- a/test/dialects/table-tests.js +++ b/test/dialects/table-tests.js @@ -21,6 +21,10 @@ Harness.test({ 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: [] }); @@ -42,6 +46,10 @@ Harness.test({ 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: [] }); @@ -63,6 +71,10 @@ Harness.test({ text : 'SELECT [user].* FROM [user]', string: 'SELECT [user].* FROM [user]' }, + oracle: { + text : 'SELECT "user".* FROM "user"', + string: 'SELECT "user".* FROM "user"' + }, params: [] }); @@ -80,6 +92,10 @@ Harness.test({ 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: [] }); @@ -97,6 +113,10 @@ Harness.test({ 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: [] }); @@ -114,6 +134,10 @@ Harness.test({ 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: [] }); @@ -131,6 +155,10 @@ Harness.test({ 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: [] }); @@ -152,6 +180,10 @@ Harness.test({ 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'] }); @@ -173,6 +205,10 @@ Harness.test({ 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'] }); @@ -194,6 +230,10 @@ Harness.test({ 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'] }); @@ -215,6 +255,10 @@ Harness.test({ 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'] }); @@ -236,6 +280,10 @@ Harness.test({ 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'] }); @@ -257,6 +305,10 @@ Harness.test({ 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'] }); @@ -278,6 +330,10 @@ Harness.test({ 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] }); @@ -299,6 +355,10 @@ Harness.test({ 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: [] }); @@ -327,6 +387,10 @@ Harness.test({ 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] }); @@ -348,6 +412,10 @@ Harness.test({ text : 'SELECT [user].[name] AS [user name], [user].[id] AS [user id] FROM [user]', string: 'SELECT [user].[name] AS [user name], [user].[id] AS [user id] FROM [user]' }, + 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: [] }); @@ -365,10 +433,14 @@ Harness.test({ text : 'SELECT `user`.`name` AS `user name` FROM `user` WHERE (`user`.`name` = ?)', string: 'SELECT `user`.`name` AS `user name` FROM `user` WHERE (`user`.`name` = \'brian\')' }, - sqlsever: { + 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'] }); @@ -390,6 +462,10 @@ Harness.test({ 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'] }); @@ -411,6 +487,10 @@ Harness.test({ 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: [] }); @@ -432,6 +512,10 @@ Harness.test({ 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: [] }); @@ -453,6 +537,10 @@ Harness.test({ 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: [] }); @@ -474,6 +562,10 @@ Harness.test({ text : 'SELECT name, id FROM user WHERE ((name <> NULL) AND (id <> NULL))', string: 'SELECT name, id FROM user WHERE ((name <> NULL) AND (id <> NULL))' }, + 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: [] }); @@ -497,6 +589,10 @@ Harness.test({ 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'] }); @@ -521,6 +617,10 @@ Harness.test({ 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] }); @@ -542,6 +642,10 @@ Harness.test({ 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: [] }); @@ -563,5 +667,9 @@ Harness.test({ text : 'SELECT [user].* FROM [user] WHERE ([user].[id] IN (SELECT [user].[id] FROM [user]))', string: 'SELECT [user].* FROM [user] WHERE ([user].[id] IN (SELECT [user].[id] FROM [user]))' }, + oracle: { + text : 'SELECT "user".* FROM "user" WHERE ("user"."id" IN (SELECT "user"."id" FROM "user"))', + string: 'SELECT "user".* FROM "user" WHERE ("user"."id" IN (SELECT "user"."id" FROM "user"))' + }, params: [] }); diff --git a/test/dialects/ternary-clause-tests.js b/test/dialects/ternary-clause-tests.js index 60c369b8..04832fbc 100644 --- a/test/dialects/ternary-clause-tests.js +++ b/test/dialects/ternary-clause-tests.js @@ -22,6 +22,10 @@ Harness.test({ 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] }); @@ -43,5 +47,9 @@ Harness.test({ text : 'SELECT [post].* FROM [post] WHERE ([post].[userId] BETWEEN (SELECT MIN([customer].[id]) AS [id_min] FROM [customer]) AND (SELECT MAX([customer].[id]) AS [id_max] FROM [customer]))', string: 'SELECT [post].* FROM [post] WHERE ([post].[userId] BETWEEN (SELECT MIN([customer].[id]) AS [id_min] FROM [customer]) AND (SELECT MAX([customer].[id]) AS [id_max] FROM [customer]))' }, + oracle: { + text : 'SELECT "post".* FROM "post" WHERE ("post"."userId" BETWEEN (SELECT MIN("customer"."id") "id_min" FROM "customer") AND (SELECT MAX("customer"."id") "id_max" FROM "customer"))', + string: 'SELECT "post".* FROM "post" WHERE ("post"."userId" BETWEEN (SELECT MIN("customer"."id") "id_min" FROM "customer") AND (SELECT MAX("customer"."id") "id_max" FROM "customer"))' + }, params: [] }); diff --git a/test/dialects/tostring-tests.js b/test/dialects/tostring-tests.js index 47b60367..53d413e8 100644 --- a/test/dialects/tostring-tests.js +++ b/test/dialects/tostring-tests.js @@ -23,6 +23,10 @@ Harness.test({ text : '([post].[content] = @1)', string: '([post].[content] = NULL)' }, + oracle: { + text : '("post"."content" = :1)', + string: '("post"."content" = NULL)' + }, params: [null] }); @@ -45,6 +49,10 @@ Harness.test({ text : '([post].[content] = @1)', string: '([post].[content] = 3.14)' }, + oracle: { + text : '("post"."content" = :1)', + string: '("post"."content" = 3.14)' + }, params: [3.14] }); @@ -67,6 +75,10 @@ Harness.test({ text : '([post].[content] = @1)', string: '([post].[content] = \'hello\'\'\')' }, + oracle: { + text : '("post"."content" = :1)', + string: '("post"."content" = \'hello\'\'\')' + }, params: ['hello\''] }); @@ -89,6 +101,10 @@ Harness.test({ text : 'SQL Server does not support arrays.', throws: true }, + oracle: { + text : 'SQL Server does not support arrays.', + throws: true + }, params: [1, '2', null] }); @@ -111,6 +127,39 @@ Harness.test({ 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')] }); @@ -139,6 +188,10 @@ Harness.test({ text : '([post].[content] = @1)', string: '([post].[content] = \'secretMessage\')' }, + oracle: { + text : '("post"."content" = :1)', + string: '("post"."content" = \'secretMessage\')' + }, params: [customObject] }); diff --git a/test/dialects/truncate-table-tests.js b/test/dialects/truncate-table-tests.js new file mode 100644 index 00000000..cfdb04da --- /dev/null +++ b/test/dialects/truncate-table-tests.js @@ -0,0 +1,29 @@ +'use strict'; + +var Harness = require('./support'); +var post = Harness.definePostTable(); + +Harness.test({ + query: post.truncate(), + pg: { + text : 'TRUNCATE TABLE "post"', + string: 'TRUNCATE TABLE "post"' + }, + sqlite: { + text : 'DELETE FROM "post"', + string: 'DELETE FROM "post"' + }, + mysql: { + text : 'TRUNCATE TABLE `post`', + string: 'TRUNCATE TABLE `post`' + }, + mssql: { + text : 'TRUNCATE TABLE [post]', + string: 'TRUNCATE TABLE [post]' + }, + oracle: { + text : 'TRUNCATE TABLE "post"', + string: 'TRUNCATE TABLE "post"' + }, + params: [] +}); diff --git a/test/dialects/unary-clause-tests.js b/test/dialects/unary-clause-tests.js index 02352545..875de382 100644 --- a/test/dialects/unary-clause-tests.js +++ b/test/dialects/unary-clause-tests.js @@ -22,6 +22,10 @@ Harness.test({ 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: [] }); @@ -43,5 +47,9 @@ Harness.test({ text : 'SELECT [post].* FROM [post] WHERE ([post].[userId] IN (SELECT [customer].[id] FROM [customer] WHERE ([customer].[age] IS NULL)))', string: 'SELECT [post].* FROM [post] WHERE ([post].[userId] IN (SELECT [customer].[id] FROM [customer] WHERE ([customer].[age] IS NULL)))' }, + oracle: { + text : 'SELECT "post".* FROM "post" WHERE ("post"."userId" IN (SELECT "customer"."id" FROM "customer" WHERE ("customer"."age" IS NULL)))', + string: 'SELECT "post".* FROM "post" WHERE ("post"."userId" IN (SELECT "customer"."id" FROM "customer" WHERE ("customer"."age" IS NULL)))' + }, params: [] }); diff --git a/test/dialects/update-tests.js b/test/dialects/update-tests.js index 090ab9ee..eb3d0b3e 100644 --- a/test/dialects/update-tests.js +++ b/test/dialects/update-tests.js @@ -3,6 +3,7 @@ var Harness = require('./support'); var post = Harness.definePostTable(); var user = Harness.defineUserTable(); +var variable = Harness.defineVariableTable(); Harness.test({ query: post.update({ @@ -24,6 +25,10 @@ Harness.test({ 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'] }); @@ -48,6 +53,10 @@ Harness.test({ 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] }); @@ -72,6 +81,10 @@ Harness.test({ 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] }); @@ -96,6 +109,10 @@ Harness.test({ 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'] }); @@ -119,6 +136,10 @@ Harness.test({ text : 'UPDATE [post] SET [content] = [user].[name] FROM [user] WHERE ([post].[userId] = [user].[id])', string: 'UPDATE [post] SET [content] = [user].[name] FROM [user] WHERE ([post].[userId] = [user].[id])' }, + 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: [] }); @@ -143,6 +164,10 @@ Harness.test({ text : 'UPDATE [post] SET [userId] = [user].[id] FROM [user] WHERE ([post].[userId] = [user].[id])', string: 'UPDATE [post] SET [userId] = [user].[id] FROM [user] WHERE ([post].[userId] = [user].[id])' }, + 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: [] }); @@ -163,5 +188,59 @@ Harness.test({ text : 'UPDATE `post` SET `content` = ?', string: 'UPDATE `post` SET `content` = x\'74657374\'' }, + oracle: { + text : 'UPDATE "post" SET "content" = :1', + string: 'UPDATE "post" SET "content" = utl_raw.cast_to_varchar2(hextoraw(\'74657374\'))' + }, params: [new Buffer('test')] }); + +// Boolean updates +Harness.test({ + query: variable.update({ + a: true, + b: false + }), + pg: { + text : 'UPDATE "variable" SET "a" = $1, "b" = $2', + string: 'UPDATE "variable" SET "a" = TRUE, "b" = FALSE' + }, + sqlite: { + text : 'UPDATE "variable" SET "a" = $1, "b" = $2', + string: 'UPDATE "variable" SET "a" = 1, "b" = 0' + }, + mysql: { + text : 'UPDATE `variable` SET `a` = ?, `b` = ?', + string: 'UPDATE `variable` SET `a` = TRUE, `b` = FALSE' + }, + oracle: { + text : 'UPDATE "variable" SET "a" = :1, "b" = :2', + string: 'UPDATE "variable" SET "a" = TRUE, "b" = FALSE' + }, + params: [true, false] +}); + +// Object updates +Harness.test({ + query: variable.update({ + a: {"id": 1, "value": 2}, + b: [{"id": 2, "value": 3}, {"id": 3, "value": 4}] + }), + pg: { + text : 'UPDATE "variable" SET "a" = $1, "b" = $2', + string: 'UPDATE "variable" SET "a" = \'{"id":1,"value":2}\', "b" = \'[{"id":2,"value":3},{"id":3,"value":4}]\'' + }, + sqlite: { + text : 'UPDATE "variable" SET "a" = $1, "b" = $2', + string: 'UPDATE "variable" SET "a" = \'{"id":1,"value":2}\', "b" = \'[{"id":2,"value":3},{"id":3,"value":4}]\'' + }, + mysql: { + text : 'UPDATE `variable` SET `a` = ?, `b` = ?', + string: 'UPDATE `variable` SET `a` = \'{"id":1,"value":2}\', `b` = (\'{"id":2,"value":3}\', \'{"id":3,"value":4}\')' + }, + oracle: { + text : 'UPDATE "variable" SET "a" = :1, "b" = :2', + string: 'UPDATE "variable" SET "a" = \'{"id":1,"value":2}\', "b" = (\'{"id":2,"value":3}\', \'{"id":3,"value":4}\')' + }, + params: [{"id": 1, "value": 2}, [{"id": 2, "value": 3}, {"id": 3, "value": 4}]] +}); \ No newline at end of file diff --git a/test/dialects/value-expression-tests.js b/test/dialects/value-expression-tests.js index 1a842257..d5c7fcd4 100644 --- a/test/dialects/value-expression-tests.js +++ b/test/dialects/value-expression-tests.js @@ -24,6 +24,10 @@ Harness.test({ 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] }); @@ -46,6 +50,10 @@ Harness.test({ 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'] }); @@ -69,6 +77,10 @@ Harness.test({ 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] }); @@ -91,6 +103,10 @@ Harness.test({ text : 'SELECT ((([variable].[a] * [variable].[a]) + ([variable].[b] * [variable].[b])) = ([variable].[c] * [variable].[c])) FROM [variable]', string: 'SELECT ((([variable].[a] * [variable].[a]) + ([variable].[b] * [variable].[b])) = ([variable].[c] * [variable].[c])) FROM [variable]' }, + 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: [] }); @@ -98,16 +114,46 @@ 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\')', + 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\')', + 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\')', + string: 'SELECT `post`.`id` FROM `post` WHERE (`post`.`content` = x\'74657374\')' + }, + oracle: { + text : 'SELECT "post"."id" FROM "post" WHERE ("post"."content" = :1)', + string: 'SELECT "post"."id" FROM "post" WHERE ("post"."content" = utl_raw.cast_to_varchar2(hextoraw(\'74657374\')))' }, params: [new Buffer('test')] }); +// concat tests +Harness.test({ + query: post.select(post.content.concat(post.tags)), + pg: { + text : 'SELECT ("post"."content" || "post"."tags") FROM "post"', + string: 'SELECT ("post"."content" || "post"."tags") FROM "post"' + }, + sqlite: { + text : 'SELECT ("post"."content" || "post"."tags") FROM "post"', + string: 'SELECT ("post"."content" || "post"."tags") FROM "post"' + }, + mysql: { + text : 'SELECT (`post`.`content` || `post`.`tags`) FROM `post`', + string: 'SELECT (`post`.`content` || `post`.`tags`) FROM `post`' + }, + mssql: { + text : 'SELECT ([post].[content] + [post].[tags]) FROM [post]', + string: 'SELECT ([post].[content] + [post].[tags]) FROM [post]' + }, + oracle: { + text : 'SELECT ("post"."content" || "post"."tags") FROM "post"', + string: 'SELECT ("post"."content" || "post"."tags") FROM "post"' + }, + params: [] +}); + diff --git a/test/dialects/where-clause-tests.js b/test/dialects/where-clause-tests.js index b25f1f35..3282b7d9 100644 --- a/test/dialects/where-clause-tests.js +++ b/test/dialects/where-clause-tests.js @@ -21,6 +21,10 @@ Harness.test({ text : 'SELECT * FROM [user] WHERE (([user].[id] IS NOT NULL) AND ([user].[name] IS NOT NULL))', string: 'SELECT * FROM [user] WHERE (([user].[id] IS NOT NULL) AND ([user].[name] IS NOT NULL))' }, + 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: [] }); @@ -42,6 +46,10 @@ Harness.test({ text : 'SELECT * FROM [user] WHERE (([user].[id] IS NOT NULL) AND ([user].[name] IS NOT NULL))', string: 'SELECT * FROM [user] WHERE (([user].[id] IS NOT NULL) AND ([user].[name] IS NOT NULL))' }, + 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: [] }); @@ -63,6 +71,10 @@ Harness.test({ text : 'SELECT * FROM [user] WHERE (([user].[id] IS NOT NULL) AND ([user].[name] IS NOT NULL))', string: 'SELECT * FROM [user] WHERE (([user].[id] IS NOT NULL) AND ([user].[name] IS NOT NULL))' }, + 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: [] }); @@ -84,6 +96,10 @@ Harness.test({ 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: [] }); @@ -105,6 +121,10 @@ Harness.test({ 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'] }); @@ -126,6 +146,10 @@ Harness.test({ 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] }); @@ -147,6 +171,10 @@ Harness.test({ 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] }); @@ -168,6 +196,10 @@ Harness.test({ text : 'SELECT [user].* FROM [user] WHERE (([user].[id] = @1) OR ([user].[name] = @2))', string: 'SELECT [user].* FROM [user] WHERE (([user].[id] = 1) OR ([user].[name] = \'a\'))' }, + oracle: { + text : 'SELECT "user".* FROM "user" WHERE (("user"."id" = :1) OR ("user"."name" = :2))', + string: 'SELECT "user".* FROM "user" WHERE (("user"."id" = 1) OR ("user"."name" = \'a\'))' + }, params: [1,'a'] }); diff --git a/test/function-tests.js b/test/function-tests.js index 076c2ed5..6c53072d 100644 --- a/test/function-tests.js +++ b/test/function-tests.js @@ -5,7 +5,12 @@ var sql = require(__dirname + '/../lib').setDialect('postgres'); var user = sql.define({ name: 'user', - columns: ['id', 'email', 'name'] + columns: [ + {name: 'id'}, + {name:'email'}, + {name: 'name'}, + {name: 'age', property: 'howOld'} + ] }); suite('function', function() { @@ -16,6 +21,14 @@ suite('function', function() { assert.equal(aliasedUpper.text, 'UPPER("user"."email") AS "upperAlias"'); }); + test('function call on aliased column', function() { + var round = sql.functions.ROUND; + var aliasedRound = round(user.howOld, 2).toQuery(); + + assert.equal(aliasedRound.text, 'ROUND("user"."age", $1)'); + assert.equal(aliasedRound.values[0], 2); + }); + test('creating function call works', function() { var upper = sql.functionCallCreator('UPPER'); var functionCall = upper('hello', 'world').toQuery(); @@ -71,4 +84,10 @@ suite('function', function() { assert.equal(query.text, 'SELECT (AVG((DISTINCT((COUNT("user"."id") + MAX("user"."id"))) - MIN("user"."id"))) * $1) FROM "user"'); assert.equal(query.values[0], 100); }); + + test('use custom function', function() { + var query = user.select(sql.function('PHRASE_TO_TSQUERY')('simple', user.name)).toQuery(); + assert.equal(query.text, 'SELECT PHRASE_TO_TSQUERY($1, "user"."name") FROM "user"'); + assert.equal(query.values[0], 'simple'); + }); }); diff --git a/test/index-tests.js b/test/index-tests.js index e5076f17..f25839e1 100644 --- a/test/index-tests.js +++ b/test/index-tests.js @@ -30,6 +30,11 @@ suite('index', function() { 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(); @@ -56,16 +61,19 @@ suite('index', function() { 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() { @@ -74,11 +82,13 @@ suite('index', function() { 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() { @@ -87,11 +97,13 @@ suite('index', function() { 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)'); @@ -105,6 +117,9 @@ suite('index', function() { 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() { @@ -140,10 +155,15 @@ suite('index', function() { 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)'); @@ -157,6 +177,14 @@ suite('index', function() { 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); }); @@ -167,4 +195,20 @@ suite('index', function() { }); }); + 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/table-tests.js b/test/table-tests.js index e6570901..73d43367 100644 --- a/test/table-tests.js +++ b/test/table-tests.js @@ -167,6 +167,19 @@ test('hasColumn', function() { assert.equal(table.hasColumn('baz'), true); }); +test('hasColumn with user-defined column property', function() { + var table = Table.define({ + name: 'blah', + columns: [{ + name: 'id', + property: 'theId' + }, {name: 'foo'}] + }); + + assert.equal(table.hasColumn('id'), true); + assert.equal(table.hasColumn('theId'), true); +}); + test('the column "from" does not overwrite the from method', function() { var table = Table.define({ name: 'foo', columns: [] }); table.addColumn('from'); @@ -235,3 +248,26 @@ test('dialects', function () { actual = foo.join(bar).on(bar.id.equals(1)).toString(); assert.equal(actual, '"foo" INNER JOIN "bar" ON ("bar"."id" = 1)'); }); + +test('limit', function () { + var user = Table.define({name: 'user', columns: ['id', 'name']}); + var query = user.limit(3); + assert.equal(query.nodes.length, 1); + assert.equal(query.nodes[0].type, 'LIMIT'); + assert.equal(query.nodes[0].count, 3); +}); + +test('offset', function () { + var user = Table.define({name: 'user', columns: ['id', 'name']}); + var query = user.offset(20); + assert.equal(query.nodes.length, 1); + assert.equal(query.nodes[0].type, 'OFFSET'); + assert.equal(query.nodes[0].count, 20); +}); + +test('order', function () { + var user = Table.define({name: 'user', columns: ['id', 'name']}); + var query = user.order(user.name); + assert.equal(query.nodes.length, 1); + assert.equal(query.nodes[0].type, 'ORDER BY'); +});