From 02adb9d3e77d1b069ce8feb7c0657892c22db1c8 Mon Sep 17 00:00:00 2001 From: soliton4 Date: Thu, 5 Feb 2015 22:59:52 +0100 Subject: [PATCH 001/153] fixes drop index statement for pg --- lib/dialect/postgres.js | 4 +--- test/dialects/indexes-tests.js | 24 ++++++++++++------------ 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/lib/dialect/postgres.js b/lib/dialect/postgres.js index 3c9ff184..3d50a7af 100644 --- a/lib/dialect/postgres.js +++ b/lib/dialect/postgres.js @@ -877,9 +877,7 @@ Postgres.prototype.visitCreateIndex = function(node) { Postgres.prototype.visitDropIndex = function(node) { var result = [ 'DROP INDEX' ]; - result.push(this.quote(node.options.indexName)); - result.push("ON"); - result.push(this.visit(node.table.toNode())); + result.push(this.quote(node.table.getSchema() || "public") + "." + this.quote(node.options.indexName)); return result; }; diff --git a/test/dialects/indexes-tests.js b/test/dialects/indexes-tests.js index 482e0d6d..9ea75ef8 100644 --- a/test/dialects/indexes-tests.js +++ b/test/dialects/indexes-tests.js @@ -124,16 +124,16 @@ Harness.test({ 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"' }, params: [] }); @@ -141,16 +141,16 @@ 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"' }, params: [] }); From 4e77f319dbcdb7b53378fdb52ab4d221aeb6ff3c Mon Sep 17 00:00:00 2001 From: soliton4 Date: Sun, 22 Feb 2015 01:18:17 +0100 Subject: [PATCH 002/153] changed " to ` for mysql --- test/dialects/indexes-tests.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/dialects/indexes-tests.js b/test/dialects/indexes-tests.js index 9ea75ef8..b950e832 100644 --- a/test/dialects/indexes-tests.js +++ b/test/dialects/indexes-tests.js @@ -128,8 +128,8 @@ Harness.test({ string: 'DROP INDEX "public"."index_name"' }, mysql: { - text : 'DROP INDEX "public"."index_name"', - string: 'DROP INDEX "public"."index_name"' + text : 'DROP INDEX `public`.`index_name`', + string: 'DROP INDEX `public`.`index_name`' }, sqlite: { text : 'DROP INDEX "public"."index_name"', @@ -145,8 +145,8 @@ Harness.test({ string: 'DROP INDEX "public"."post_id_userId"' }, mysql: { - text : 'DROP INDEX "public"."post_id_userId"', - string: 'DROP INDEX "public"."post_id_userId"' + text : 'DROP INDEX `public`.`post_id_userId`', + string: 'DROP INDEX `public`.`post_id_userId`' }, sqlite: { text : 'DROP INDEX "public"."post_id_userId"', From 63e3cbb349832fab73c2a55c6f2342dd797fde00 Mon Sep 17 00:00:00 2001 From: Dan Rzeppa Date: Sat, 28 Feb 2015 15:53:03 -0500 Subject: [PATCH 003/153] Support for temporary tables. --- lib/dialect/mssql.js | 9 ++++- lib/dialect/postgres.js | 3 +- lib/node/create.js | 9 ++++- lib/node/query.js | 2 +- lib/table.js | 2 + test/dialects/create-table-tests.js | 61 +++++++++++++++++++++++++++++ 6 files changed, 82 insertions(+), 4 deletions(-) diff --git a/lib/dialect/mssql.js b/lib/dialect/mssql.js index 1b02af2d..534a4b2c 100644 --- a/lib/dialect/mssql.js +++ b/lib/dialect/mssql.js @@ -185,7 +185,9 @@ Mssql.prototype.visitColumn = function(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 +211,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']; }; @@ -408,6 +411,10 @@ function isCreateIfNotExists(create){ 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; diff --git a/lib/dialect/postgres.js b/lib/dialect/postgres.js index a698030f..f57b7441 100644 --- a/lib/dialect/postgres.js +++ b/lib/dialect/postgres.js @@ -247,7 +247,8 @@ Postgres.prototype.visitCreate = function(create) { var table = this._queryNode.table; var col_nodes = table.columns.map(function(col) { return col.toNode(); }); - 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) { diff --git a/lib/node/create.js b/lib/node/create.js index bf535f6a..c048c3ce 100644 --- a/lib/node/create.js +++ b/lib/node/create.js @@ -3,5 +3,12 @@ var Node = require(__dirname); module.exports = Node.define({ - type: 'CREATE' + type: 'CREATE', + + constructor: function(isTemporary) { + Node.call(this); + + this.options = { isTemporary: isTemporary}; + }, + }); diff --git a/lib/node/query.js b/lib/node/query.js index 255332fd..fe91c98d 100644 --- a/lib/node/query.js +++ b/lib/node/query.js @@ -276,7 +276,7 @@ var Query = Node.define({ this.add(createIndex); return createIndex; } else { - return this.add(new Create()); + return this.add(new Create(this.table.isTemporary)); } }, diff --git a/lib/table.js b/lib/table.js index 5763ecf7..2ed1d6be 100644 --- a/lib/table.js +++ b/lib/table.js @@ -15,6 +15,7 @@ var Table = function(config) { this._name = config.name; this._initialConfig = config; this.columnWhiteList = !!config.columnWhiteList; + this.isTemporary=!!config.isTemporary this.snakeToCamel = !!config.snakeToCamel; this.columns = []; this.table = this; @@ -130,6 +131,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; }; diff --git a/test/dialects/create-table-tests.js b/test/dialects/create-table-tests.js index 9993a2fa..33ba0f8d 100644 --- a/test/dialects/create-table-tests.js +++ b/test/dialects/create-table-tests.js @@ -320,3 +320,64 @@ 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)' + }, + 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: [] +}); + From 2987e139c8ded57e686aa772475d56cfdca1c086 Mon Sep 17 00:00:00 2001 From: brianc Date: Tue, 10 Mar 2015 12:11:34 -0400 Subject: [PATCH 004/153] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d7bcc7d7..76f388b9 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "author": "brianc ", "name": "sql", "description": "sql builder", - "version": "0.47.0", + "version": "0.48.0", "homepage": "https://github.com/brianc/node-sql", "repository": { "type": "git", From 5566e04b046e83d3d8d05bd920d836554ae38ebe Mon Sep 17 00:00:00 2001 From: Dorian Johnson <2014@dorianj.net> Date: Tue, 17 Mar 2015 11:16:42 -0500 Subject: [PATCH 005/153] Add support for accessing PG composite types (UDT) As per http://www.postgresql.org/docs/9.4/static/rowtypes.html#AEN7836 No attempt at supporting these in UPDATE is attempted here. --- lib/dialect/postgres.js | 5 +++- lib/node/column.js | 4 ++++ lib/table.js | 14 +++++++++++ test/dialects/subfield-tests.js | 42 +++++++++++++++++++++++++++++++++ test/dialects/support.js | 11 +++++++++ 5 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 test/dialects/subfield-tests.js diff --git a/lib/dialect/postgres.js b/lib/dialect/postgres.js index 7c650507..1fd2dec2 100644 --- a/lib/dialect/postgres.js +++ b/lib/dialect/postgres.js @@ -654,7 +654,7 @@ Postgres.prototype.visitColumn = function(columnNode) { txt.push('DISTINCT('); } } - if(!inInsertUpdateClause && !this.visitingReturning && !this._visitingCreate && !this._visitingAlter) { + if(!inInsertUpdateClause && !this.visitingReturning && !this._visitingCreate && !this._visitingAlter && !columnNode.subfieldContainer) { if(table.alias) { txt.push(this.quote(table.alias)); } else { @@ -686,6 +686,9 @@ Postgres.prototype.visitColumn = function(columnNode) { } } else { + if (columnNode.subfieldContainer) { + txt.push('(' + this.visitColumn(columnNode.subfieldContainer) + ').'); + } txt.push(this.quote(columnNode.name)); } if(closeParen) { diff --git a/lib/node/column.js b/lib/node/column.js index 6234b2f2..03b58ad5 100644 --- a/lib/node/column.js +++ b/lib/node/column.js @@ -19,6 +19,10 @@ module.exports = Node.define({ this.primaryKey = config.primaryKey; this.notNull = config.notNull; 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; }, as: function(alias) { diff --git a/lib/table.js b/lib/table.js index 2ed1d6be..cafbf213 100644 --- a/lib/table.js +++ b/lib/table.js @@ -67,6 +67,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)) + .object() + .value() + } } return col; 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/support.js b/test/dialects/support.js index e985a2da..e95e3175 100644 --- a/test/dialects/support.js +++ b/test/dialects/support.js @@ -93,6 +93,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', From 9334290f62f95673297ec9022bb8981257ad33dd Mon Sep 17 00:00:00 2001 From: klaemo Date: Tue, 24 Mar 2015 11:06:26 +0100 Subject: [PATCH 006/153] readme: mention dialects --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 3a5493b8..454466bc 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,10 @@ Maybe it's still not fun, but at least it's _less not fun_. //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', From 1385c77cf94ff326d86d93afb7ee4ceafc953ea6 Mon Sep 17 00:00:00 2001 From: Dick Fickling Date: Thu, 26 Mar 2015 14:32:55 -0700 Subject: [PATCH 007/153] Add ON DUPLICATE KEY support for MySQL --- lib/dialect/mssql.js | 4 ++++ lib/dialect/mysql.js | 14 ++++++++++++++ lib/dialect/postgres.js | 5 +++++ lib/dialect/sqlite.js | 4 ++++ lib/node/onDuplicate.js | 7 +++++++ lib/node/query.js | 16 ++++++++++++++++ test/dialects/insert-tests.js | 23 +++++++++++++++++++++++ 7 files changed, 73 insertions(+) create mode 100644 lib/node/onDuplicate.js diff --git a/lib/dialect/mssql.js b/lib/dialect/mssql.js index c15d615e..056a962f 100644 --- a/lib/dialect/mssql.js +++ b/lib/dialect/mssql.js @@ -357,6 +357,10 @@ Mssql.prototype.visitQueryHelper=function(actions,targets,filters){ // return "SHOW INDEX FROM " + tableName; //}; +Mssql.prototype.visitOnDuplicate = function(onDuplicate) { + throw new Error('MSSQL does not allow onDuplicate clause.'); +}; + Mssql.prototype.visitReturning = function() { // TODO: need to add some code to the INSERT clause to support this since its the equivalent of the OUTPUT clause // in MS SQL which appears before the values, not at the end of the statement. diff --git a/lib/dialect/mysql.js b/lib/dialect/mysql.js index 0cc24fb2..fc4c6cfc 100644 --- a/lib/dialect/mysql.js +++ b/lib/dialect/mysql.js @@ -31,6 +31,20 @@ 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.visitReturning = function() { throw new Error('MySQL does not allow returning clause.'); }; diff --git a/lib/dialect/postgres.js b/lib/dialect/postgres.js index 7c650507..893c3ff0 100644 --- a/lib/dialect/postgres.js +++ b/lib/dialect/postgres.js @@ -117,6 +117,7 @@ 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 'FOR UPDATE' : return this.visitForUpdate(); case 'FOR SHARE' : return this.visitForShare(); case 'TABLE' : return this.visitTable(node); @@ -826,6 +827,10 @@ Postgres.prototype.visitReturning = function(returning) { return r; }; +Postgres.prototype.visitOnDuplicate = function(onDuplicate) { + throw new Error('PostgreSQL does not allow onDuplicate clause.'); +}; + Postgres.prototype.visitModifier = function(node) { return [node.type, node.count.type ? this.visit(node.count) : node.count]; }; diff --git a/lib/dialect/sqlite.js b/lib/dialect/sqlite.js index 63da1354..0f210390 100644 --- a/lib/dialect/sqlite.js +++ b/lib/dialect/sqlite.js @@ -38,6 +38,10 @@ 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.visitReturning = function() { throw new Error('SQLite does not allow returning clause.'); }; diff --git a/lib/node/onDuplicate.js b/lib/node/onDuplicate.js new file mode 100644 index 00000000..38146793 --- /dev/null +++ b/lib/node/onDuplicate.js @@ -0,0 +1,7 @@ +'use strict'; + +var Node = require(__dirname); + +module.exports = Node.define({ + type: 'ONDUPLICATE' +}); diff --git a/lib/node/query.js b/lib/node/query.js index fe91c98d..53802f90 100644 --- a/lib/node/query.js +++ b/lib/node/query.js @@ -15,6 +15,7 @@ var Insert = require('./insert'); var Update = require('./update'); var Delete = require('./delete'); var Returning = require('./returning'); +var OnDuplicate = require('./onDuplicate'); var ForUpdate = require('./forUpdate'); var ForShare = require('./forShare'); var Create = require('./create'); @@ -258,6 +259,21 @@ var Query = Node.define({ 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))); + }); + + return self.add(onDuplicate); + }, + + forUpdate: function() { assert(typeof this._select !== 'undefined', 'FOR UPDATE can be used only in a select statement'); this.add(new ForUpdate()); diff --git a/test/dialects/insert-tests.js b/test/dialects/insert-tests.js index f9ef0efb..cb18458d 100644 --- a/test/dialects/insert-tests.js +++ b/test/dialects/insert-tests.js @@ -481,6 +481,29 @@ Harness.test({ 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 + }, + params: ['test', 2, 'testupdate'] +}); + Harness.test({ query: post.insert([]), From e2c4c9f3c0f944e8d33d9bd5d5c1269fee909cd8 Mon Sep 17 00:00:00 2001 From: Paul King Date: Mon, 13 Apr 2015 09:42:32 -0600 Subject: [PATCH 008/153] Adding iRegex and notIRegex. --- lib/node/valueExpression.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/node/valueExpression.js b/lib/node/valueExpression.js index 876872cd..6933fa11 100644 --- a/lib/node/valueExpression.js +++ b/lib/node/valueExpression.js @@ -138,8 +138,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('->>'), From fc20418628ffd34a3627bb93b52cf4f81fca85a9 Mon Sep 17 00:00:00 2001 From: Paul King Date: Mon, 13 Apr 2015 11:31:04 -0600 Subject: [PATCH 009/153] Tests for iregex and notIregex. --- test/binary-clause-tests.js | 2 ++ test/dialects/regex-tests.js | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+) 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/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'] +}); From 158a09f1af22cc2a9c19d3ffe7b9dc4e4751bfb0 Mon Sep 17 00:00:00 2001 From: brianc Date: Wed, 15 Apr 2015 09:37:01 -0400 Subject: [PATCH 010/153] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 76f388b9..8fa320c8 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "author": "brianc ", "name": "sql", "description": "sql builder", - "version": "0.48.0", + "version": "0.49.0", "homepage": "https://github.com/brianc/node-sql", "repository": { "type": "git", From 79a138be0fcb1ec38d2a9f33743c1652b4c4e213 Mon Sep 17 00:00:00 2001 From: brianc Date: Wed, 15 Apr 2015 09:55:28 -0400 Subject: [PATCH 011/153] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8fa320c8..2ba0905b 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "author": "brianc ", "name": "sql", "description": "sql builder", - "version": "0.49.0", + "version": "0.50.0", "homepage": "https://github.com/brianc/node-sql", "repository": { "type": "git", From 2ccd60eabb4fa2af89f37cc3752dd7351b4e8844 Mon Sep 17 00:00:00 2001 From: Benjamin Diemert Date: Thu, 16 Apr 2015 18:24:55 +0200 Subject: [PATCH 012/153] Define specific constraint on references column (Postgres only) When you're defining columns in a table that references others columns in you database, you can now add a specific constraint such as "ON DELETE CASCADE". The definition will looks like : references: { table: "another_table", column: "another_table_primary_key", constraint: "ON DELETE CASCADE" } --- lib/dialect/postgres.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/dialect/postgres.js b/lib/dialect/postgres.js index cd560aac..0d4652cb 100644 --- a/lib/dialect/postgres.js +++ b/lib/dialect/postgres.js @@ -730,8 +730,12 @@ 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(columnNode.references.hasOwnProperty('constraint') + ? ' REFERENCES ' + columnNode.references.table + '(' + + columnNode.references.column + ') ' + columnNode.references.constraint + : ' REFERENCES ' + columnNode.references.table + '(' + + columnNode.references.column + ')' + ); } } } From 01fe04ab9fe7e00c8957f541692e80db64958cd3 Mon Sep 17 00:00:00 2001 From: Dan Rzeppa Date: Thu, 30 Apr 2015 11:05:41 -0400 Subject: [PATCH 013/153] Added TRUNCATE TABLE command. --- lib/dialect/postgres.js | 8 ++++++++ lib/dialect/sqlite.js | 6 ++++++ lib/node/query.js | 5 +++++ lib/node/truncate.js | 12 ++++++++++++ lib/table.js | 6 ++++++ test/dialects/truncate-table-tests.js | 25 +++++++++++++++++++++++++ 6 files changed, 62 insertions(+) create mode 100644 lib/node/truncate.js create mode 100644 test/dialects/truncate-table-tests.js diff --git a/lib/dialect/postgres.js b/lib/dialect/postgres.js index cd560aac..31b9bb3c 100644 --- a/lib/dialect/postgres.js +++ b/lib/dialect/postgres.js @@ -107,6 +107,7 @@ Postgres.prototype.visit = function(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 'ALIAS' : return this.visitAlias(node); case 'ALTER' : return this.visitAlter(node); case 'CAST' : return this.visitCast(node); @@ -280,6 +281,12 @@ 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.visitAlias = function(alias) { var result = [this.visit(alias.value) + ' AS ' + this.quote(alias.alias)]; return result; @@ -545,6 +552,7 @@ Postgres.prototype.visitQuery = function(queryNode) { case "UPDATE": case "CREATE": case "DROP": + case "TRUNCATE": case "ALTER": actions.push(node); missingFrom = false; diff --git a/lib/dialect/sqlite.js b/lib/dialect/sqlite.js index 0f210390..8530159f 100644 --- a/lib/dialect/sqlite.js +++ b/lib/dialect/sqlite.js @@ -34,6 +34,12 @@ Sqlite.prototype.visitDropColumn = function() { throw new Error('SQLite does not allow dropping columns.'); }; +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.'); }; diff --git a/lib/node/query.js b/lib/node/query.js index 53802f90..40309d5a 100644 --- a/lib/node/query.js +++ b/lib/node/query.js @@ -20,6 +20,7 @@ var ForUpdate = require('./forUpdate'); var ForShare = require('./forShare'); var Create = require('./create'); var Drop = require('./drop'); +var Truncate = require('./truncate'); var Alter = require('./alter'); var AddColumn = require('./addColumn'); var DropColumn = require('./dropColumn'); @@ -307,6 +308,10 @@ var Query = Node.define({ } }, + truncate: function() { + return this.add(new Truncate(this.table)); + }, + alter: function() { return this.add(new Alter()); }, diff --git a/lib/node/truncate.js b/lib/node/truncate.js new file mode 100644 index 00000000..24bc168d --- /dev/null +++ b/lib/node/truncate.js @@ -0,0 +1,12 @@ +'use strict'; + +var Node = require(__dirname); + +module.exports = Node.define({ + type: 'TRUNCATE', + + constructor: function(table) { + Node.call(this); + this.add(table); + } +}); diff --git a/lib/table.js b/lib/table.js index cafbf213..a5407c32 100644 --- a/lib/table.js +++ b/lib/table.js @@ -234,6 +234,12 @@ Table.prototype.drop = function() { return query; }; +Table.prototype.truncate = function() { + var query = new Query(this); + query.truncate.apply(query, arguments); + return query; +}; + Table.prototype.alter = function() { var query = new Query(this); query.alter.apply(query, arguments); diff --git a/test/dialects/truncate-table-tests.js b/test/dialects/truncate-table-tests.js new file mode 100644 index 00000000..a6197b9c --- /dev/null +++ b/test/dialects/truncate-table-tests.js @@ -0,0 +1,25 @@ +'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]' + }, + params: [] +}); From 01c046ab2d01e2185e0138ed6dc1d5fb891b9700 Mon Sep 17 00:00:00 2001 From: brianc Date: Sun, 3 May 2015 14:44:11 -0400 Subject: [PATCH 014/153] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2ba0905b..9c8520d2 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "author": "brianc ", "name": "sql", "description": "sql builder", - "version": "0.50.0", + "version": "0.51.0", "homepage": "https://github.com/brianc/node-sql", "repository": { "type": "git", From ef5433b3a6a0e58249c4f39f2791e51f1813c428 Mon Sep 17 00:00:00 2001 From: Dan Rzeppa Date: Wed, 13 May 2015 11:09:05 -0400 Subject: [PATCH 015/153] - Added proper DISTINCT functionality. --- lib/dialect/mssql.js | 24 +++--------- lib/dialect/postgres.js | 41 +++++++++++++++++++- lib/node/distinct.js | 7 ++++ lib/node/query.js | 5 +++ lib/node/select.js | 4 +- test/dialects/distinct-tests.js | 66 +++++++++++++++++++++++++++++++++ test/dialects/insert-tests.js | 21 +++++++++++ 7 files changed, 147 insertions(+), 21 deletions(-) create mode 100644 lib/node/distinct.js diff --git a/lib/dialect/mssql.js b/lib/dialect/mssql.js index 056a962f..5b60ab64 100644 --- a/lib/dialect/mssql.js +++ b/lib/dialect/mssql.js @@ -263,25 +263,10 @@ 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 Date: Wed, 13 May 2015 09:19:00 -0700 Subject: [PATCH 016/153] Add scripts to makefile --- Makefile | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) 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 From a0b3461915d65c64638f58e1616a57cd2d70151a Mon Sep 17 00:00:00 2001 From: brianc Date: Wed, 13 May 2015 09:19:04 -0700 Subject: [PATCH 017/153] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9c8520d2..4c1ce65f 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "author": "brianc ", "name": "sql", "description": "sql builder", - "version": "0.51.0", + "version": "0.52.0", "homepage": "https://github.com/brianc/node-sql", "repository": { "type": "git", From 4912b080a389695ef66e4af1b55503389222260f Mon Sep 17 00:00:00 2001 From: Brian C Date: Wed, 13 May 2015 09:30:33 -0700 Subject: [PATCH 018/153] Update README.md --- README.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 454466bc..23e66ef5 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,13 @@ 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 @@ -102,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) From 2a2bf5e6f6f305e5a5c6ef5bef9dffbb93361833 Mon Sep 17 00:00:00 2001 From: Bergwinkl Thomas Date: Tue, 19 May 2015 13:38:53 +0200 Subject: [PATCH 019/153] don't use alias in binary clause --- lib/dialect/postgres.js | 4 ++++ test/dialects/binary-clause-tests.js | 23 +++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/lib/dialect/postgres.js b/lib/dialect/postgres.js index 09c148e1..b37cbf58 100644 --- a/lib/dialect/postgres.js +++ b/lib/dialect/postgres.js @@ -373,6 +373,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) { diff --git a/test/dialects/binary-clause-tests.js b/test/dialects/binary-clause-tests.js index 28b4a155..31c271e6 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: { @@ -25,6 +27,27 @@ Harness.test({ params: [] }); +Harness.test({ + query: customerAlias.select(customerAlias.name_alias.plus(customerAlias.age_alias)), + pg: { + text : 'SELECT ("customer"."name" + "customer"."age") FROM "customer"', + string: 'SELECT ("customer"."name" + "customer"."age") FROM "customer"' + }, + sqlite: { + text : 'SELECT ("customer"."name" + "customer"."age") FROM "customer"', + string: 'SELECT ("customer"."name" + "customer"."age") FROM "customer"' + }, + mysql: { + text : 'SELECT (`customer`.`name` + `customer`.`age`) FROM `customer`', + string: 'SELECT (`customer`.`name` + `customer`.`age`) FROM `customer`' + }, + mssql: { + text : 'SELECT ([customer].[name] + [customer].[age]) FROM [customer]', + string: 'SELECT ([customer].[name] + [customer].[age]) FROM [customer]' + }, + params: [] +}); + Harness.test({ query: post.select(post.content.plus('!')).where(post.userId. in (customer.subQuery().select(customer.id))), pg: { From 853a549bddf3d121fc9859639fd7fcacedd537fb Mon Sep 17 00:00:00 2001 From: Jacob Chang Date: Sun, 7 Jun 2015 19:23:59 +0800 Subject: [PATCH 020/153] add quote to named subquery alias --- lib/dialect/postgres.js | 2 +- package.json | 2 +- test/dialects/join-tests.js | 16 ++++++++-------- test/dialects/literal-tests.js | 12 ++++++------ test/dialects/subquery-tests.js | 16 ++++++++-------- 5 files changed, 24 insertions(+), 24 deletions(-) diff --git a/lib/dialect/postgres.js b/lib/dialect/postgres.js index 09c148e1..19830dd8 100644 --- a/lib/dialect/postgres.js +++ b/lib/dialect/postgres.js @@ -624,7 +624,7 @@ Postgres.prototype.visitSubquery = function(queryNode) { } var alias = queryNode.alias; - return ['(' + subQuery.output.join(' ') + ')' + (alias ? ' ' + alias : '')]; + return ['(' + subQuery.output.join(' ') + ')' + (alias ? ' ' + this.quote(alias) : '')]; }; Postgres.prototype.visitTable = function(tableNode) { diff --git a/package.json b/package.json index 4c1ce65f..dd389f0c 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "author": "brianc ", "name": "sql", "description": "sql builder", - "version": "0.52.0", + "version": "0.53.0", "homepage": "https://github.com/brianc/node-sql", "repository": { "type": "git", diff --git a/test/dialects/join-tests.js b/test/dialects/join-tests.js index 88792acf..bd5fc3b6 100644 --- a/test/dialects/join-tests.js +++ b/test/dialects/join-tests.js @@ -133,20 +133,20 @@ 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])' + 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])' }, params: [] }); diff --git a/test/dialects/literal-tests.js b/test/dialects/literal-tests.js index e1940f5a..424bedcf 100644 --- a/test/dialects/literal-tests.js +++ b/test/dialects/literal-tests.js @@ -45,16 +45,16 @@ 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`' }, params: [] }); diff --git a/test/dialects/subquery-tests.js b/test/dialects/subquery-tests.js index 8a5aa16e..89e141b1 100644 --- a/test/dialects/subquery-tests.js +++ b/test/dialects/subquery-tests.js @@ -54,20 +54,20 @@ Harness.test({ 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]' }, params: [] }); From a7783455343c2b36e359540008869a1630841171 Mon Sep 17 00:00:00 2001 From: John Shen Date: Sat, 11 Jul 2015 17:20:18 -0400 Subject: [PATCH 021/153] adding on-delete constraints to create table --- lib/dialect/postgres.js | 3 +++ test/dialects/create-table-tests.js | 28 ++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/lib/dialect/postgres.js b/lib/dialect/postgres.js index 09c148e1..85eaf232 100644 --- a/lib/dialect/postgres.js +++ b/lib/dialect/postgres.js @@ -749,6 +749,9 @@ Postgres.prototype.visitColumn = function(columnNode) { ' require a table and column)'); txt.push(' REFERENCES ' + columnNode.references.table + '(' + columnNode.references.column + ')'); + if (!!columnNode.references.onDelete) { + txt.push(' ON DELETE ' + columnNode.references.onDelete.toUpperCase()) + } } } } diff --git a/test/dialects/create-table-tests.js b/test/dialects/create-table-tests.js index 33ba0f8d..af50af1c 100644 --- a/test/dialects/create-table-tests.js +++ b/test/dialects/create-table-tests.js @@ -274,6 +274,34 @@ Harness.test({ 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)' + }, + params: [] +}); + Harness.test({ query: Table.define({ name: 'post', From f3664f30e5a7c0455252ffe79df21682ece75bfd Mon Sep 17 00:00:00 2001 From: John Shen Date: Sat, 11 Jul 2015 17:42:40 -0400 Subject: [PATCH 022/153] ensure that only cascade/restrict options are available for on delete clauses --- lib/dialect/postgres.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/dialect/postgres.js b/lib/dialect/postgres.js index 85eaf232..fdae849b 100644 --- a/lib/dialect/postgres.js +++ b/lib/dialect/postgres.js @@ -750,7 +750,10 @@ Postgres.prototype.visitColumn = function(columnNode) { txt.push(' REFERENCES ' + columnNode.references.table + '(' + columnNode.references.column + ')'); if (!!columnNode.references.onDelete) { + if (columnNode.references.onDelete.toUpperCase() === "CASCADE" || + columnNode.references.onDelete.toUpperCase() === "RESTRICT") { txt.push(' ON DELETE ' + columnNode.references.onDelete.toUpperCase()) + } } } } From d75f204cfba1be00572dc38f135592e172a1e04f Mon Sep 17 00:00:00 2001 From: Eduardo Dutra Date: Wed, 15 Jul 2015 10:53:01 -0300 Subject: [PATCH 023/153] Implemented Rename Columns for MSSQL --- lib/dialect/mssql.js | 22 ++++++++++------------ test/dialects/alter-table-tests.js | 24 ++++++++---------------- 2 files changed, 18 insertions(+), 28 deletions(-) diff --git a/lib/dialect/mssql.js b/lib/dialect/mssql.js index 5b60ab64..c859ac82 100644 --- a/lib/dialect/mssql.js +++ b/lib/dialect/mssql.js @@ -99,19 +99,17 @@ Mssql.prototype.visitAlter = function(alter) { // 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(); diff --git a/test/dialects/alter-table-tests.js b/test/dialects/alter-table-tests.js index d9e83d94..423ee029 100644 --- a/test/dialects/alter-table-tests.js +++ b/test/dialects/alter-table-tests.js @@ -178,10 +178,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 +199,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 +220,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 +250,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\'' } }); From 62d87d1baa9fc1c7f6250b6e2197e88d4551b4c1 Mon Sep 17 00:00:00 2001 From: brianc Date: Wed, 15 Jul 2015 09:30:02 -0500 Subject: [PATCH 024/153] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index dd389f0c..8e8a7fde 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "author": "brianc ", "name": "sql", "description": "sql builder", - "version": "0.53.0", + "version": "0.54.0", "homepage": "https://github.com/brianc/node-sql", "repository": { "type": "git", From 395114a75f8e1d77c77edd2a266c6355188bbfdc Mon Sep 17 00:00:00 2001 From: brianc Date: Wed, 15 Jul 2015 09:35:16 -0500 Subject: [PATCH 025/153] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8e8a7fde..22864027 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "author": "brianc ", "name": "sql", "description": "sql builder", - "version": "0.54.0", + "version": "0.55.0", "homepage": "https://github.com/brianc/node-sql", "repository": { "type": "git", From b89604b4e4347f7c7366272ee96b2a35eb37038e Mon Sep 17 00:00:00 2001 From: Eduardo Dutra Date: Thu, 16 Jul 2015 19:40:19 -0300 Subject: [PATCH 026/153] basic queries working --- README.md | 2 +- lib/dialect/index.js | 2 ++ lib/dialect/oracle.js | 42 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 lib/dialect/oracle.js diff --git a/README.md b/README.md index 23e66ef5..193ab2be 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. 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/oracle.js b/lib/dialect/oracle.js new file mode 100644 index 00000000..d0dc5271 --- /dev/null +++ b/lib/dialect/oracle.js @@ -0,0 +1,42 @@ +'use strict'; + +var util = require('util'); +var assert = require('assert'); + +var Oracle = function() { + this.output = []; + this.params = []; +}; + +var Postgres = require(__dirname + '/postgres'); + +util.inherits(Oracle, Postgres); + +Oracle.prototype._myClass = Oracle; + +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.visitTable = function(tableNode) { + var table = tableNode.table; + var txt=""; + if(table.getSchema()) { + txt = this.quote(table.getSchema()); + txt += '.'; + } + txt += this.quote(table.getName()); + if(table.alias) { + txt += ' ' + this.quote(table.alias); + } + return [txt]; +}; + + +module.exports = Oracle; From 544d2526d715d0bad4efb91fad83d3db6ea81e9a Mon Sep 17 00:00:00 2001 From: Alex Jordan Date: Sat, 18 Jul 2015 17:55:42 -0700 Subject: [PATCH 027/153] Add license metadata in package.json --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 22864027..0a4a51c8 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "description": "sql builder", "version": "0.55.0", "homepage": "https://github.com/brianc/node-sql", + "license": "MIT", "repository": { "type": "git", "url": "git://github.com/brianc/node-sql.git" From fb0304ef25b1f5669e7a7c797cfb1267a309d614 Mon Sep 17 00:00:00 2001 From: Eduardo Dutra Date: Tue, 21 Jul 2015 09:13:50 -0300 Subject: [PATCH 028/153] Oracle - all tests done --- lib/dialect/oracle.js | 213 ++++++++++++++++++++++++ lib/dialect/postgres.js | 21 ++- test/dialects/aggregate-tests.js | 76 +++++++++ test/dialects/alias-tests.js | 12 ++ test/dialects/alter-table-tests.js | 20 ++- test/dialects/binary-clause-tests.js | 12 ++ test/dialects/case-tests.js | 37 ++++ test/dialects/cast-tests.js | 24 +++ test/dialects/clause-ordering-tests.js | 16 ++ test/dialects/create-table-tests.js | 88 ++++++++-- test/dialects/delete-tests.js | 20 +++ test/dialects/distinct-tests.js | 20 +++ test/dialects/drop-table-tests.js | 16 ++ test/dialects/from-clause-tests.js | 16 ++ test/dialects/group-by-tests.js | 20 +++ test/dialects/having-tests.js | 12 ++ test/dialects/in-clause-tests.js | 28 ++++ test/dialects/indexes-tests.js | 28 ++++ test/dialects/insert-tests.js | 98 ++++++++++- test/dialects/join-tests.js | 24 +++ test/dialects/join-to-tests.js | 12 ++ test/dialects/limit-and-offset-tests.js | 8 + test/dialects/literal-tests.js | 12 ++ test/dialects/matches-test.js | 4 + test/dialects/namespace-tests.js | 20 +++ test/dialects/not-in-clause-tests.js | 28 ++++ test/dialects/order-tests.js | 40 +++++ test/dialects/schema-tests.js | 24 +++ test/dialects/select-tests.js | 12 ++ test/dialects/shortcut-tests.js | 24 +++ test/dialects/subquery-tests.js | 20 +++ test/dialects/support.js | 3 +- test/dialects/table-tests.js | 110 +++++++++++- test/dialects/ternary-clause-tests.js | 8 + test/dialects/tostring-tests.js | 16 ++ test/index-tests.js | 28 ++++ 36 files changed, 1136 insertions(+), 34 deletions(-) diff --git a/lib/dialect/oracle.js b/lib/dialect/oracle.js index d0dc5271..763b2c5a 100644 --- a/lib/dialect/oracle.js +++ b/lib/dialect/oracle.js @@ -10,10 +10,13 @@ var Oracle = function() { var Postgres = require(__dirname + '/postgres'); +var Mssql = require(__dirname + '/mssql'); + util.inherits(Oracle, Postgres); Oracle.prototype._myClass = Oracle; +Oracle.prototype._aliasText = ' '; Oracle.prototype._getParameterPlaceholder = function(index, value) { /* jshint unused: false */ return ':' + index; @@ -38,5 +41,215 @@ Oracle.prototype.visitTable = function(tableNode) { return [txt]; }; +Oracle.prototype.visitCascade = function() { + return ['CASCADE CONSTRAINTS']; +}; + +Oracle.prototype.visitRestrict = function() { + throw new Error('Oracle do not support RESTRICT in DROP TABLE'); +}; + +Oracle.prototype.visitDrop = function(drop) { + if (!isDropIfExists(drop)) { + return Oracle.super_.prototype.visitDrop.call(this, drop); + } + // Implement our own drop if exists: + // PostgreSQL: DROP TABLE IF EXISTS "group" + // Oracle: + // BEGIN + // EXECUTE IMMEDIATE 'DROP TABLE POST'; + // EXCEPTION + // WHEN OTHERS THEN + // IF SQLCODE != -942 THEN + // RAISE; + // END IF; + // END; + var table = this._queryNode.table; + var tableResult=this.visit(table.toNode()); + + var dropResult = ['DROP TABLE']; + dropResult.push(tableResult); + + return ["BEGIN EXECUTE IMMEDIATE '"+dropResult.join(' ')+"'; EXCEPTION WHEN OTHERS THEN IF SQLCODE != -942 THEN RAISE; END IF; END;"]; +}; + +Oracle.prototype.visitCreate = function(create) { + var isNotExists=isCreateIfNotExists(create) + //var isTemporary=isCreateTemporary(create) + var createText = Oracle.super_.prototype.visitCreate.call(this, create); + if (isNotExists) { + // Implement our own create if not exists: + // PostgreSQL: CREATE TABLE IF NOT EXISTS "group" ("id" varchar(100)) + // Oracle: + // BEGIN + // EXECUTE IMMEDIATE 'CREATE TABLE ...'; + // EXCEPTION + // WHEN OTHERS THEN + // IF SQLCODE != -955 THEN + // RAISE; + // END IF; + // END; + + createText = "BEGIN EXECUTE IMMEDIATE '"+createText.join(' ').replace(' IF NOT EXISTS','')+"'; EXCEPTION WHEN OTHERS THEN IF SQLCODE != -955 THEN RAISE; END IF; END;"; + } + + return createText; +}; + +Oracle.prototype.visitBinary = function(binary) { + if(binary.operator === '@@'){ + var self = this; + var text = '(INSTR (' + this.visit(binary.left) + ', '; + text += this.visit(binary.right); + text += ') > 0)'; + return [text]; + } + + if (!isRightSideArray(binary)){ + return Oracle.super_.prototype.visitBinary.call(this, binary); + } + if (binary.operator=='IN' || binary.operator=='NOT IN'){ + return Oracle.super_.prototype.visitBinary.call(this, binary); + } + throw new Error('Oracle does not support arrays in this type of expression.'); +}; + +Oracle.prototype.visitModifier = function(node) { + var ret = Oracle.super_.prototype.visitModifier.call(this, node); + if (ret.indexOf('OFFSET') >= 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.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); +} + + +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; +}; module.exports = Oracle; diff --git a/lib/dialect/postgres.js b/lib/dialect/postgres.js index a6ff8a14..01e314cb 100644 --- a/lib/dialect/postgres.js +++ b/lib/dialect/postgres.js @@ -162,6 +162,8 @@ Postgres.prototype.visit = function(node) { }; Postgres.prototype._quoteCharacter = '"'; +Postgres.prototype._aliasText = ' AS '; + Postgres.prototype.quote = function(word, quoteCharacter) { var q; if (quoteCharacter) { @@ -296,7 +298,7 @@ Postgres.prototype.visitDistinct = function(truncate) { }; 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; }; @@ -636,7 +638,7 @@ Postgres.prototype.visitTable = function(tableNode) { } txt += this.quote(table.getName()); if(table.alias) { - txt += ' AS ' + this.quote(table.alias); + txt += this._aliasText + this.quote(table.alias); } return [txt]; }; @@ -693,7 +695,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) { @@ -715,7 +717,7 @@ Postgres.prototype.visitColumn = function(columnNode) { } } if(inSelectClause && (columnNode.alias || columnNode.property !== columnNode.name)) { - txt.push(' AS ' + this.quote(columnNode.alias || columnNode.property)); + txt.push(this._aliasText + this.quote(columnNode.alias || columnNode.property)); } if(this._visitingCreate || this._visitingAddColumn) { assert(columnNode.dataType, 'dataType missing for column ' + columnNode.name + @@ -747,8 +749,13 @@ 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) + ')'); if (!!columnNode.references.onDelete) { if (columnNode.references.onDelete.toUpperCase() === "CASCADE" || columnNode.references.onDelete.toUpperCase() === "RESTRICT") { @@ -840,7 +847,7 @@ Postgres.prototype.visitJoin = function(join) { 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('')]; }; diff --git a/test/dialects/aggregate-tests.js b/test/dialects/aggregate-tests.js index a58397cc..d67d775a 100644 --- a/test/dialects/aggregate-tests.js +++ b/test/dialects/aggregate-tests.js @@ -23,6 +23,10 @@ Harness.test({ 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: [] }); @@ -44,6 +48,10 @@ Harness.test({ 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: [] }); @@ -65,6 +73,10 @@ Harness.test({ 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: [] }); @@ -145,6 +169,10 @@ Harness.test({ text : 'SELECT COUNT(`customer`.*) AS `customer_count` FROM `customer`', string: 'SELECT COUNT(`customer`.*) 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..f558260b 100644 --- a/test/dialects/alias-tests.js +++ b/test/dialects/alias-tests.js @@ -21,6 +21,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 +46,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 +71,9 @@ 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] }); diff --git a/test/dialects/alter-table-tests.js b/test/dialects/alter-table-tests.js index 423ee029..f733b2f2 100644 --- a/test/dialects/alter-table-tests.js +++ b/test/dialects/alter-table-tests.js @@ -274,16 +274,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 COLUMN "userId" int REFERENCES "user"("id")', + string: 'ALTER TABLE "post" ADD COLUMN "userId" int REFERENCES "user"("id")' }, params: [] }); @@ -302,5 +306,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 COLUMN "picture" varchar(100)', + string: 'ALTER TABLE "post" ADD COLUMN "picture" varchar(100)' + }, params: [] }); diff --git a/test/dialects/binary-clause-tests.js b/test/dialects/binary-clause-tests.js index 28b4a155..88950e22 100644 --- a/test/dialects/binary-clause-tests.js +++ b/test/dialects/binary-clause-tests.js @@ -22,6 +22,10 @@ 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: [] }); @@ -43,6 +47,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 +72,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..046ee4b2 100644 --- a/test/dialects/case-tests.js +++ b/test/dialects/case-tests.js @@ -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] }); @@ -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] }); @@ -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] }); @@ -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] }); @@ -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..8f407f3d 100644 --- a/test/dialects/cast-tests.js +++ b/test/dialects/cast-tests.js @@ -22,6 +22,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 +47,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 +73,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 +99,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 +125,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 +151,9 @@ 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: [] }); 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 af50af1c..f9c3e2fa 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,10 @@ 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)' } }); @@ -229,16 +261,20 @@ Harness.test({ }] }).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"))', + string: 'CREATE TABLE "post" ("userId" int REFERENCES "user"("id"))' }, 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"))', + string: 'CREATE TABLE "post" ("userId" int REFERENCES "user"("id"))' }, 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`))', + string: 'CREATE TABLE `post` (`userId` int REFERENCES `user`(`id`))' + }, + oracle: { + text : 'CREATE TABLE "post" ("userId" int REFERENCES "user"("id"))', + string: 'CREATE TABLE "post" ("userId" int REFERENCES "user"("id"))' }, params: [] }); @@ -260,16 +296,20 @@ 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: [] }); @@ -288,16 +328,20 @@ Harness.test({ }] }).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)' + 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)' + 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)' + 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: [] }); @@ -323,6 +367,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: [] }); @@ -378,6 +426,10 @@ Harness.test({ 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: [] }); 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-tests.js b/test/dialects/distinct-tests.js index d340d400..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,6 +46,10 @@ 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: [] }); @@ -65,6 +73,10 @@ Harness.test({ 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: [] }); @@ -86,6 +98,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: [] }); @@ -107,6 +123,10 @@ Harness.test({ 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/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 b950e832..a32c235f 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,6 +114,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: [] }); @@ -118,6 +134,10 @@ Harness.test({ sqlite: { text : 'No columns defined!', throws: true + }, + oracle: { + text : 'No columns defined!', + throws: true } }); @@ -135,6 +155,10 @@ Harness.test({ text : 'DROP INDEX "public"."index_name"', string: 'DROP INDEX "public"."index_name"' }, + oracle: { + text : 'DROP INDEX "index_name"', + string: 'DROP INDEX "index_name"' + }, params: [] }); @@ -152,5 +176,9 @@ Harness.test({ 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 6685e6f6..b472f51d 100644 --- a/test/dialects/insert-tests.js +++ b/test/dialects/insert-tests.js @@ -18,6 +18,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,6 +39,14 @@ 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'] }); @@ -55,6 +67,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 +91,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 +118,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 +146,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 +179,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 +204,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 +226,9 @@ Harness.test({ mssql: { throws: true }, + oracle: { + throws: true + }, params: [] }); @@ -208,6 +247,9 @@ Harness.test({ mssql: { throws: true }, + oracle: { + throws: true + }, params: [] }); @@ -226,6 +268,9 @@ Harness.test({ mssql: { throws: true }, + oracle: { + throws: true + }, params: [] }); @@ -244,6 +289,9 @@ Harness.test({ mssql: { throws: true }, + oracle: { + throws: true + }, params: [] }); @@ -262,6 +310,9 @@ Harness.test({ mssql: { throws: true }, + oracle: { + throws: true + }, params: [] }); @@ -292,7 +343,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 +377,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 +404,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 +430,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 +452,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,6 +478,10 @@ 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%'] }); @@ -426,6 +503,10 @@ Harness.test({ 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: [] }); @@ -448,6 +529,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] }); @@ -472,6 +557,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] }); @@ -499,6 +588,10 @@ 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')] }); @@ -522,6 +615,9 @@ Harness.test({ mssql: { throws: true }, + oracle: { + throws: true + }, params: ['test', 2, 'testupdate'] }); diff --git a/test/dialects/join-tests.js b/test/dialects/join-tests.js index bd5fc3b6..abebdb01 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: [] }); @@ -148,5 +168,9 @@ Harness.test({ 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: [] }); 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 424bedcf..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: [] }); @@ -56,5 +64,9 @@ Harness.test({ 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..888f27f3 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,9 @@ 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: [] }); 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..59fdd786 100644 --- a/test/dialects/select-tests.js +++ b/test/dialects/select-tests.js @@ -22,6 +22,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 +43,13 @@ 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: [] }); \ 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/subquery-tests.js b/test/dialects/subquery-tests.js index 89e141b1..2d5df342 100644 --- a/test/dialects/subquery-tests.js +++ b/test/dialects/subquery-tests.js @@ -27,6 +27,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,6 +52,10 @@ 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: [] }); @@ -69,6 +77,10 @@ Harness.test({ 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 +105,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 +130,9 @@ 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: [] }); diff --git a/test/dialects/support.js b/test/dialects/support.js index e95e3175..e8c68837 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 = { 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..8b9763b4 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] }); diff --git a/test/index-tests.js b/test/index-tests.js index e5076f17..0893a7ef 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); }); From 2751e77264f75dfc101a7313cfec27ee11ab904c Mon Sep 17 00:00:00 2001 From: Eduardo Dutra Date: Tue, 21 Jul 2015 10:19:54 -0300 Subject: [PATCH 029/153] oracle dialect implementaion complete --- test/dialects/tostring-tests.js | 8 +++++++ test/dialects/truncate-table-tests.js | 4 ++++ test/dialects/unary-clause-tests.js | 8 +++++++ test/dialects/update-tests.js | 28 ++++++++++++++++++++++ test/dialects/value-expression-tests.js | 20 ++++++++++++++++ test/dialects/where-clause-tests.js | 32 +++++++++++++++++++++++++ 6 files changed, 100 insertions(+) diff --git a/test/dialects/tostring-tests.js b/test/dialects/tostring-tests.js index 8b9763b4..215cb05b 100644 --- a/test/dialects/tostring-tests.js +++ b/test/dialects/tostring-tests.js @@ -127,6 +127,10 @@ 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')] }); @@ -155,6 +159,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 index a6197b9c..cfdb04da 100644 --- a/test/dialects/truncate-table-tests.js +++ b/test/dialects/truncate-table-tests.js @@ -21,5 +21,9 @@ Harness.test({ 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..5639c3a3 100644 --- a/test/dialects/update-tests.js +++ b/test/dialects/update-tests.js @@ -24,6 +24,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 +52,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 +80,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 +108,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 +135,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 +163,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 +187,9 @@ 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')] }); diff --git a/test/dialects/value-expression-tests.js b/test/dialects/value-expression-tests.js index 1a842257..5eaae5ae 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: [] }); @@ -108,6 +124,10 @@ Harness.test({ text : 'SELECT `post`.`id` FROM `post` WHERE (`post`.`content` = ?)', string: 'SELECT `post`.`id` FROM `post` WHERE (`post`.`content` = x\'74657374\')', }, + oracle: { + text : 'SELECT "post"."id" FROM "post" WHERE ("post"."content" = :1)', + string: 'SELECT "post"."id" FROM "post" WHERE ("post"."content" = utl_raw.cast_to_varchar2(hextoraw(\'74657374\')))', + }, params: [new Buffer('test')] }); 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'] }); From d42d02448e8ddeb3f337f490469cb738a55978ad Mon Sep 17 00:00:00 2001 From: Bergwinkl Thomas Date: Wed, 29 Jul 2015 16:11:24 +0200 Subject: [PATCH 030/153] add DISTINCT ON support for Postgres --- lib/dialect/postgres.js | 18 +++++++++++++++++- lib/node/distinctOn.js | 7 +++++++ lib/node/query.js | 28 ++++++++++++++++++++++++++++ test/dialects/distinct-on-tests.js | 24 ++++++++++++++++++++++++ 4 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 lib/node/distinctOn.js create mode 100644 test/dialects/distinct-on-tests.js diff --git a/lib/dialect/postgres.js b/lib/dialect/postgres.js index ac6e96c1..15a57f73 100644 --- a/lib/dialect/postgres.js +++ b/lib/dialect/postgres.js @@ -109,6 +109,7 @@ Postgres.prototype.visit = function(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); @@ -180,9 +181,20 @@ Postgres.prototype.quote = function(word, quoteCharacter) { Postgres.prototype.visitSelect = function(select) { var result = ['SELECT'] + if (select.isDistinct) result.push('DISTINCT'); - result.push(select.nodes.map(this.visit.bind(this)).join(', ')); + + 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; }; @@ -295,6 +307,10 @@ Postgres.prototype.visitDistinct = function(truncate) { 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)]; return result; diff --git a/lib/node/distinctOn.js b/lib/node/distinctOn.js new file mode 100644 index 00000000..a208dfde --- /dev/null +++ b/lib/node/distinctOn.js @@ -0,0 +1,7 @@ +'use strict'; + +var Node = require(__dirname); + +module.exports = Node.define({ + type: 'DISTINCT ON', +}); diff --git a/lib/node/query.js b/lib/node/query.js index d32b6379..5672259c 100644 --- a/lib/node/query.js +++ b/lib/node/query.js @@ -22,6 +22,7 @@ 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'); @@ -317,6 +318,33 @@ var Query = Node.define({ 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()); }, 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: [] +}); + From 393c8d2b8f5357073d3483c738f49a7dc82e7793 Mon Sep 17 00:00:00 2001 From: brianc Date: Wed, 29 Jul 2015 23:20:56 -0500 Subject: [PATCH 031/153] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 22864027..f8ae3e0c 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "author": "brianc ", "name": "sql", "description": "sql builder", - "version": "0.55.0", + "version": "0.56.0", "homepage": "https://github.com/brianc/node-sql", "repository": { "type": "git", From 40272b541d7944336285689cb767ceddcf293764 Mon Sep 17 00:00:00 2001 From: brianc Date: Wed, 29 Jul 2015 23:21:28 -0500 Subject: [PATCH 032/153] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 838c25f5..9650363e 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "author": "brianc ", "name": "sql", "description": "sql builder", - "version": "0.56.0", + "version": "0.57.0", "homepage": "https://github.com/brianc/node-sql", "license": "MIT", "repository": { From e7f039e92acbd8f923389337f54bfc686d775e66 Mon Sep 17 00:00:00 2001 From: Eduardo Dutra Date: Wed, 12 Aug 2015 12:37:10 -0300 Subject: [PATCH 033/153] Fix #224 --- lib/dialect/postgres.js | 5 ++++- test/dialects/alias-tests.js | 26 ++++++++++++++++++++++++++ test/function-tests.js | 15 ++++++++++++++- 3 files changed, 44 insertions(+), 2 deletions(-) diff --git a/lib/dialect/postgres.js b/lib/dialect/postgres.js index 01e314cb..d7b93b59 100644 --- a/lib/dialect/postgres.js +++ b/lib/dialect/postgres.js @@ -656,6 +656,7 @@ Postgres.prototype.visitColumn = function(columnNode) { && !this.visitingCase && !this._visitingJoin ); + var inFunctionCall = this._visitingFunctionCall; var txt = []; var closeParen = 0; if(inSelectClause && (!table.alias || !!columnNode.alias)) { @@ -716,7 +717,7 @@ Postgres.prototype.visitColumn = function(columnNode) { txt.push(')'); } } - if(inSelectClause && (columnNode.alias || columnNode.property !== columnNode.name)) { + if(inSelectClause && !inFunctionCall && (columnNode.alias || columnNode.property !== columnNode.name)) { txt.push(this._aliasText + this.quote(columnNode.alias || columnNode.property)); } if(this._visitingCreate || this._visitingAddColumn) { @@ -769,7 +770,9 @@ Postgres.prototype.visitColumn = function(columnNode) { }; Postgres.prototype.visitFunctionCall = function(functionCall) { + this._visitingFunctionCall = true; var txt = functionCall.name + '(' + functionCall.nodes.map(this.visit.bind(this)).join(', ') + ')'; + this._visitingFunctionCall = false; return [txt]; }; diff --git a/test/dialects/alias-tests.js b/test/dialects/alias-tests.js index f558260b..f650cd5a 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')), @@ -77,3 +78,28 @@ Harness.test({ }, 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] +}); diff --git a/test/function-tests.js b/test/function-tests.js index 076c2ed5..77ef0087 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(); From 967c0411faee516a75870e03af5ddb73e84e84da Mon Sep 17 00:00:00 2001 From: Eduardo Dutra Date: Wed, 12 Aug 2015 15:11:08 -0300 Subject: [PATCH 034/153] Fix #186 --- lib/dialect/postgres.js | 5 +- test/dialects/cast-tests.js | 155 ++++++++++++++++++++++++++++++++++++ 2 files changed, 159 insertions(+), 1 deletion(-) diff --git a/lib/dialect/postgres.js b/lib/dialect/postgres.js index d7b93b59..85e5ccde 100644 --- a/lib/dialect/postgres.js +++ b/lib/dialect/postgres.js @@ -316,7 +316,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; }; @@ -657,6 +659,7 @@ Postgres.prototype.visitColumn = function(columnNode) { && !this._visitingJoin ); var inFunctionCall = this._visitingFunctionCall; + var inCast = this._visitingCast; var txt = []; var closeParen = 0; if(inSelectClause && (!table.alias || !!columnNode.alias)) { @@ -717,7 +720,7 @@ Postgres.prototype.visitColumn = function(columnNode) { txt.push(')'); } } - if(inSelectClause && !inFunctionCall && (columnNode.alias || columnNode.property !== columnNode.name)) { + 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) { diff --git a/test/dialects/cast-tests.js b/test/dialects/cast-tests.js index 8f407f3d..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({ @@ -157,3 +158,157 @@ Harness.test({ }, 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: [] +}); From 45b929795bde084dc0ce252f23de0efaaa8f09c9 Mon Sep 17 00:00:00 2001 From: Dan Rzeppa Date: Wed, 12 Aug 2015 15:20:56 -0400 Subject: [PATCH 035/153] Override the implementation of the standard LENGTH function for mssql which calls it LEN. --- lib/dialect/mssql.js | 8 ++++++++ test/dialects/function-tests.js | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 test/dialects/function-tests.js diff --git a/lib/dialect/mssql.js b/lib/dialect/mssql.js index c859ac82..f03d1ae2 100644 --- a/lib/dialect/mssql.js +++ b/lib/dialect/mssql.js @@ -234,6 +234,14 @@ Mssql.prototype.visitDrop = function(drop) { return ['IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES '+whereClause+') BEGIN '+dropResult.join(' ')+' END']; }; +Mssql.prototype.visitFunctionCall = function(functionCall) { + var name=functionCall.name + // override the LENGTH function since mssql calls it LEN + if (name=="LENGTH") name="LEN" + var txt = name + '(' + functionCall.nodes.map(this.visit.bind(this)).join(', ') + ')'; + return [txt]; +}; + Mssql.prototype.visitOrderBy = function(orderBy) { var result=Mssql.super_.prototype.visitOrderBy.call(this, orderBy); var offsetNode=orderBy.msSQLOffsetNode; diff --git a/test/dialects/function-tests.js b/test/dialects/function-tests.js new file mode 100644 index 00000000..da826dc9 --- /dev/null +++ b/test/dialects/function-tests.js @@ -0,0 +1,32 @@ +'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: [] +}); + From 761ebd4a3de16125929d0b2eb8d0471217fa104b Mon Sep 17 00:00:00 2001 From: brianc Date: Wed, 12 Aug 2015 14:53:48 -0500 Subject: [PATCH 036/153] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9650363e..4354f1d1 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "author": "brianc ", "name": "sql", "description": "sql builder", - "version": "0.57.0", + "version": "0.58.0", "homepage": "https://github.com/brianc/node-sql", "license": "MIT", "repository": { From 279600a10e16c12641d98124a5f142612f31a6b5 Mon Sep 17 00:00:00 2001 From: Eduardo Dutra Date: Wed, 12 Aug 2015 17:05:48 -0300 Subject: [PATCH 037/153] implements creation of index with columns in descending direction as proposed in #178. Added unit tests to this case. Closes #178 --- lib/dialect/postgres.js | 5 +++- lib/node/createIndex.js | 3 +- test/dialects/indexes-tests.js | 51 ++++++++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 2 deletions(-) diff --git a/lib/dialect/postgres.js b/lib/dialect/postgres.js index 85e5ccde..3eb76b70 100644 --- a/lib/dialect/postgres.js +++ b/lib/dialect/postgres.js @@ -912,7 +912,10 @@ Postgres.prototype.visitCreateIndex = function(node) { "ON", tableName, "(" + node.options.columns.reduce(function(result, col) { - return result.concat(this.quote(col.name)); + var column = col.name ? col.name : col.value.name; + var direction = col.direction ? ' ' + col.direction.text : ''; + var res = result.concat(this.quote(column) + direction); + return res; }.bind(this), []) + ")" ]); 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/test/dialects/indexes-tests.js b/test/dialects/indexes-tests.js index a32c235f..86b50449 100644 --- a/test/dialects/indexes-tests.js +++ b/test/dialects/indexes-tests.js @@ -121,6 +121,57 @@ Harness.test({ 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: { From 6ccdf829c2ec80f33e6310f581f5a18fca696616 Mon Sep 17 00:00:00 2001 From: Eduardo Dutra Date: Wed, 12 Aug 2015 17:58:20 -0300 Subject: [PATCH 038/153] Merged with master to get changes of PR #256 --- lib/dialect/mssql.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/dialect/mssql.js b/lib/dialect/mssql.js index f03d1ae2..1d5f30df 100644 --- a/lib/dialect/mssql.js +++ b/lib/dialect/mssql.js @@ -235,10 +235,12 @@ Mssql.prototype.visitDrop = function(drop) { }; Mssql.prototype.visitFunctionCall = function(functionCall) { + this._visitingFunctionCall = true; var name=functionCall.name // override the LENGTH function since mssql calls it LEN if (name=="LENGTH") name="LEN" var txt = name + '(' + functionCall.nodes.map(this.visit.bind(this)).join(', ') + ')'; + this._visitingFunctionCall = false; return [txt]; }; From c9500b505ec0669bfadef93df9312b7ba7a328ea Mon Sep 17 00:00:00 2001 From: Brad Dunbar Date: Wed, 12 Aug 2015 22:01:04 -0400 Subject: [PATCH 039/153] Add limit function to tables. --- lib/table.js | 6 ++++++ test/table-tests.js | 11 +++++++++++ 2 files changed, 17 insertions(+) diff --git a/lib/table.js b/lib/table.js index a5407c32..8532a30a 100644 --- a/lib/table.js +++ b/lib/table.js @@ -297,4 +297,10 @@ Table.prototype.indexes = function() { return new Query(this).indexes(); }; +Table.prototype.limit = function () { + var query = new Query(this); + query.limit.apply(query, arguments); + return query; +}; + module.exports = Table; diff --git a/test/table-tests.js b/test/table-tests.js index e6570901..d0ecdf35 100644 --- a/test/table-tests.js +++ b/test/table-tests.js @@ -235,3 +235,14 @@ 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); +}); From da6150eefe8ffd22117a825790696fa2badf2b4f Mon Sep 17 00:00:00 2001 From: Eduardo Dutra Date: Thu, 13 Aug 2015 16:55:01 -0300 Subject: [PATCH 040/153] Addes Create View Support - closes #245 --- lib/dialect/postgres.js | 37 ++++++++++++++++++-- lib/node/createView.js | 13 +++++++ lib/node/query.js | 6 ++++ test/dialects/create-view-tests.js | 54 ++++++++++++++++++++++++++++++ 4 files changed, 107 insertions(+), 3 deletions(-) create mode 100644 lib/node/createView.js create mode 100644 test/dialects/create-view-tests.js diff --git a/lib/dialect/postgres.js b/lib/dialect/postgres.js index 01e314cb..e542fb9f 100644 --- a/lib/dialect/postgres.js +++ b/lib/dialect/postgres.js @@ -71,10 +71,20 @@ Postgres.prototype.getQuery = function(queryNode) { queryNode = queryNode.select(queryNode.star()); } this.output = this.visit(queryNode); - + + //if is a create view, must replace paramaters with values + if (this.output.indexOf('CREATE VIEW') > -1) { + var previousFlagStatus = this._disableParameterPlaceholders; + this._disableParameterPlaceholders = true; + this.output = []; + this.output = this.visit(queryNode); + this.params = []; + this._disableParameterPlaceholders = previousFlagStatus; + } + // create the query object var query = { text: this.output.join(' '), values: this.params }; - + // reset the internal state of this builder this.output = []; this.params = []; @@ -142,7 +152,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 'POSTFIX UNARY' : return this.visitPostfixUnary(node); case 'PREFIX UNARY' : return this.visitPrefixUnary(node); case 'BINARY' : return this.visitBinary(node); @@ -547,6 +558,8 @@ Postgres.prototype.visitQuery = function(queryNode) { // 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 = []; @@ -554,6 +567,7 @@ Postgres.prototype.visitQuery = function(queryNode) { var node = queryNode.nodes[i]; switch(node.type) { case "SELECT": + isSelect = true; case "DELETE": actions.push(node); break; @@ -573,6 +587,9 @@ Postgres.prototype.visitQuery = function(queryNode) { missingFrom = false; targets.push(node); break; + case "CREATE VIEW": + createView = node; + break; default: filters.push(node); break; @@ -581,10 +598,18 @@ 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) { targets.push(new From().add(queryNode.table)); } + if (createView) { + if (isSelect) { + actions.unshift(createView); + } else { + throw new Error('Create View requires a Select.'); + } + } return this.visitQueryHelper(actions,targets,filters) }; @@ -926,6 +951,12 @@ Postgres.prototype.visitDropIndex = function(node) { return result; }; +Postgres.prototype.visitCreateView = function(createView) { + //console.log('createView: ' + createView); + var result = ['CREATE VIEW', this.quote(createView.options.viewName), 'AS']; + return result; +}; + /** * Broken out as a separate function so that dialects that derive from this class can still use this functionality. * diff --git a/lib/node/createView.js b/lib/node/createView.js new file mode 100644 index 00000000..bc52684f --- /dev/null +++ b/lib/node/createView.js @@ -0,0 +1,13 @@ +'use strict'; + +var Node = require(__dirname); + +module.exports = Node.define({ + type: 'CREATE VIEW', + + constructor: function(viewName) { + Node.call(this); + + this.options = { viewName: viewName}; + } +}); diff --git a/lib/node/query.js b/lib/node/query.js index d32b6379..4fb88091 100644 --- a/lib/node/query.js +++ b/lib/node/query.js @@ -38,6 +38,7 @@ var Indexes = require('./indexes'); var CreateIndex = require('./createIndex'); var DropIndex = require('./dropIndex'); var Table = require('./table'); +var CreateView = require('./createView'); var Modifier = Node.define({ constructor: function(table, type, count) { @@ -432,6 +433,11 @@ var Query = Node.define({ table: this.table }); return this.add(this.indexesClause); + }, + + createView: function(viewName) { + this.add(new CreateView(viewName)); + return this; } }); diff --git a/test/dialects/create-view-tests.js b/test/dialects/create-view-tests.js new file mode 100644 index 00000000..314d0192 --- /dev/null +++ b/test/dialects/create-view-tests.js @@ -0,0 +1,54 @@ +'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)' + } +}); From ac98e770a3be8037531b75684de5fce2c9d1da79 Mon Sep 17 00:00:00 2001 From: Eduardo Dutra Date: Thu, 13 Aug 2015 17:34:20 -0300 Subject: [PATCH 041/153] Added tests for error raised in non-SELECT create view attempts --- test/dialects/create-view-tests.js | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/test/dialects/create-view-tests.js b/test/dialects/create-view-tests.js index 314d0192..786158b1 100644 --- a/test/dialects/create-view-tests.js +++ b/test/dialects/create-view-tests.js @@ -52,3 +52,29 @@ Harness.test({ 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: [] +}); From 5255605cfd981bc0662dbf5b4e7c8da036e5ea30 Mon Sep 17 00:00:00 2001 From: Dan Rzeppa Date: Sat, 22 Aug 2015 02:23:40 -0400 Subject: [PATCH 042/153] Added LEFT and RIGHT functions. --- lib/dialect/sqlite.js | 28 ++++++++++++++++++ lib/functions.js | 2 ++ test/dialects/function-tests.js | 50 +++++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+) diff --git a/lib/dialect/sqlite.js b/lib/dialect/sqlite.js index 8530159f..17874ec5 100644 --- a/lib/dialect/sqlite.js +++ b/lib/dialect/sqlite.js @@ -34,6 +34,34 @@ Sqlite.prototype.visitDropColumn = function() { throw new Error('SQLite does not allow dropping columns.'); }; +Sqlite.prototype.visitFunctionCall = function(functionCall) { + var _this=this + + 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 + } + + 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(); + else txt = name + '(' + functionCall.nodes.map(this.visit.bind(this)).join(', ') + ')'; + return [txt]; +}; + Sqlite.prototype.visitTruncate = function(truncate) { var result = ['DELETE FROM']; result = result.concat(truncate.nodes.map(this.visit.bind(this))); diff --git a/lib/functions.js b/lib/functions.js index 084eaa4c..6a39c9b0 100644 --- a/lib/functions.js +++ b/lib/functions.js @@ -36,10 +36,12 @@ var aggregateFunctions = [ var scalarFunctions = [ 'ABS', 'COALESCE', + 'LEFT', 'LENGTH', 'LOWER', 'LTRIM', 'RANDOM', + 'RIGHT', 'ROUND', 'RTRIM', 'SUBSTR', diff --git a/test/dialects/function-tests.js b/test/dialects/function-tests.js index da826dc9..702d8c63 100644 --- a/test/dialects/function-tests.js +++ b/test/dialects/function-tests.js @@ -30,3 +30,53 @@ Harness.test({ 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] +}); + From a7114582354ec6d1bf1cd915927653deb50e806a Mon Sep 17 00:00:00 2001 From: Nikhil Ranjan Date: Mon, 31 Aug 2015 18:04:00 +0530 Subject: [PATCH 043/153] Added notBeteen dialect --- lib/node/valueExpression.js | 1 + test/dialects/alias-tests.js | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/lib/node/valueExpression.js b/lib/node/valueExpression.js index 169fb89f..428af90d 100644 --- a/lib/node/valueExpression.js +++ b/lib/node/valueExpression.js @@ -153,6 +153,7 @@ var ValueExpressionMixin = function() { in : inMethod, notIn : notInMethod, between : ternaryMethod('BETWEEN', 'AND'), + notBetween : ternaryMethod('NOT BETWEEN', 'AND'), at : atMethod, contains : binaryMethod('@>'), containedBy : binaryMethod('<@'), diff --git a/test/dialects/alias-tests.js b/test/dialects/alias-tests.js index 6981aaa6..a8540846 100644 --- a/test/dialects/alias-tests.js +++ b/test/dialects/alias-tests.js @@ -53,3 +53,20 @@ Harness.test({ }, params: [10, 20] }); + +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] +}); From a486c9f32be41c36be9331dc83cf4f556711f979 Mon Sep 17 00:00:00 2001 From: Brad Dunbar Date: Mon, 31 Aug 2015 13:32:57 -0400 Subject: [PATCH 044/153] Simplify onDelete conditional. Put onDelete in a variable and only call #toUpperCase once. --- lib/dialect/postgres.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/dialect/postgres.js b/lib/dialect/postgres.js index 01e314cb..af17e37a 100644 --- a/lib/dialect/postgres.js +++ b/lib/dialect/postgres.js @@ -749,19 +749,19 @@ Postgres.prototype.visitColumn = function(columnNode) { columnNode.name + ' (REFERENCES statements within CREATE TABLE and ADD COLUMN statements' + ' require a table and column)'); - + txt.push(' REFERENCES '); if(columnNode.references.schema) { txt.push(this.quote(columnNode.references.schema) + '.'); } txt.push(this.quote(columnNode.references.table) + '(' + this.quote(columnNode.references.column) + ')'); - if (!!columnNode.references.onDelete) { - if (columnNode.references.onDelete.toUpperCase() === "CASCADE" || - columnNode.references.onDelete.toUpperCase() === "RESTRICT") { - txt.push(' ON DELETE ' + columnNode.references.onDelete.toUpperCase()) - } - } + + var onDelete = columnNode.references.onDelete; + if (onDelete) onDelete = onDelete.toUpperCase(); + if (onDelete === 'CASCADE' || onDelete === 'RESTRICT') { + txt.push(' ON DELETE ' + onDelete) + } } } } From d0c15622624f974d1fc30660aee640a441fa3df5 Mon Sep 17 00:00:00 2001 From: Brad Dunbar Date: Mon, 31 Aug 2015 13:50:09 -0400 Subject: [PATCH 045/153] Allow passing plain query as subquery. --- lib/dialect/postgres.js | 1 + test/dialects/subquery-tests.js | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/lib/dialect/postgres.js b/lib/dialect/postgres.js index 01e314cb..588d382a 100644 --- a/lib/dialect/postgres.js +++ b/lib/dialect/postgres.js @@ -542,6 +542,7 @@ Postgres.prototype.visitOverlap = function(overlap) { }; Postgres.prototype.visitQuery = function(queryNode) { + if (this._queryNode) return this.visitSubquery(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 diff --git a/test/dialects/subquery-tests.js b/test/dialects/subquery-tests.js index 2d5df342..e27beecf 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( From 4034ce2033d9df3779a4a4eb87dfc9d4d49608a8 Mon Sep 17 00:00:00 2001 From: Brad Dunbar Date: Mon, 31 Aug 2015 21:57:31 -0400 Subject: [PATCH 046/153] Consolidate, add two more methods. --- lib/table.js | 81 ++++++++++++--------------------------------- test/table-tests.js | 20 ++++++++--- 2 files changed, 38 insertions(+), 63 deletions(-) diff --git a/lib/table.js b/lib/table.js index 8532a30a..f9c0b88c 100644 --- a/lib/table.js +++ b/lib/table.js @@ -182,12 +182,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); @@ -210,42 +204,6 @@ 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() { - 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.truncate = function() { - var query = new Query(this); - query.truncate.apply(query, arguments); - return query; -}; - -Table.prototype.alter = function() { - var query = new Query(this); - query.alter.apply(query, arguments); - return query; -}; - Table.prototype.toNode = function() { return new TableNode(this); }; @@ -275,32 +233,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(); }; -Table.prototype.limit = function () { - var query = new Query(this); - query.limit.apply(query, arguments); - return query; -}; +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/test/table-tests.js b/test/table-tests.js index d0ecdf35..66c3bdd3 100644 --- a/test/table-tests.js +++ b/test/table-tests.js @@ -237,12 +237,24 @@ test('dialects', function () { }); test('limit', function () { - var user = Table.define({ - name: 'user', - columns: ['id', 'name'] - }); + 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'); +}); From 63a8f99e61eb45f3cee99e758eeb13480288b3a1 Mon Sep 17 00:00:00 2001 From: Brad Dunbar Date: Fri, 11 Sep 2015 06:41:12 -0400 Subject: [PATCH 047/153] Function calls don't need the sql instance. --- lib/functions.js | 14 ++++++-------- lib/index.js | 7 ++----- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/lib/functions.js b/lib/functions.js index 084eaa4c..b5d1bf5e 100644 --- a/lib/functions.js +++ b/lib/functions.js @@ -4,19 +4,17 @@ var sliced = require('sliced'); var FunctionCall = require(__dirname + '/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) { var functions = _.reduce(functionNames, function(reducer, name) { - reducer[name] = getFunctionCallCreator(name, sql); + reducer[name] = getFunctionCallCreator(name); return reducer; }, {}); return functions; @@ -56,8 +54,8 @@ var textsearchFunctions = ['TS_RANK','TS_RANK_CD', 'PLAINTO_TSQUERY', 'TO_TSQUER var standardFunctionNames = aggregateFunctions.concat(scalarFunctions).concat(hstoreFunction).concat(textsearchFunctions); // 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.getStandardFunctions = getStandardFunctions; diff --git a/lib/index.js b/lib/index.js index 75d1c2e7..372de60d 100644 --- a/lib/index.js +++ b/lib/index.js @@ -16,7 +16,7 @@ var Sql = function(dialect) { this.setDialect(dialect || DEFAULT_DIALECT); // attach the standard SQL functions to this instance - this.functions = functions.getStandardFunctions(this); + this.functions = functions.getStandardFunctions(); }; // Define a table @@ -30,11 +30,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)); }; }; From 76b1d591fffbcac7308e0c166cc128e794c4c357 Mon Sep 17 00:00:00 2001 From: brianc Date: Mon, 14 Sep 2015 10:10:14 -0700 Subject: [PATCH 048/153] Add parens to tests --- test/dialects/create-view-tests.js | 40 +++++++++++++++--------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/test/dialects/create-view-tests.js b/test/dialects/create-view-tests.js index 786158b1..977f540a 100644 --- a/test/dialects/create-view-tests.js +++ b/test/dialects/create-view-tests.js @@ -7,24 +7,24 @@ var user = Harness.defineUserTable(); 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"' + 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"' + 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`' + 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]' + 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"' + text : '(CREATE VIEW "allUsersView" AS SELECT "user".* FROM "user")', + string: '(CREATE VIEW "allUsersView" AS SELECT "user".* FROM "user")' } }); @@ -32,24 +32,24 @@ Harness.test({ 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)' + 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)' + 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)' + 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)' + 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)' + 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))' } }); From 9d5937b86d1b9673e05c37689af26b6136d78918 Mon Sep 17 00:00:00 2001 From: brianc Date: Mon, 14 Sep 2015 10:10:30 -0700 Subject: [PATCH 049/153] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4354f1d1..761075e7 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "author": "brianc ", "name": "sql", "description": "sql builder", - "version": "0.58.0", + "version": "0.59.0", "homepage": "https://github.com/brianc/node-sql", "license": "MIT", "repository": { From 4bbf8432f2158f1d1b5ef3cb70b72b444c80ef90 Mon Sep 17 00:00:00 2001 From: Eduardo Dutra Date: Mon, 14 Sep 2015 14:58:38 -0300 Subject: [PATCH 050/153] merge conflict resolves for fix-186 --- lib/dialect/sqlite.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/dialect/sqlite.js b/lib/dialect/sqlite.js index 17874ec5..fdabf71a 100644 --- a/lib/dialect/sqlite.js +++ b/lib/dialect/sqlite.js @@ -37,6 +37,8 @@ Sqlite.prototype.visitDropColumn = function() { 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)) @@ -59,6 +61,8 @@ Sqlite.prototype.visitFunctionCall = function(functionCall) { if (name == "LEFT") txt = _left(); else if (name == "RIGHT") txt = _right(); else txt = name + '(' + functionCall.nodes.map(this.visit.bind(this)).join(', ') + ')'; + + this._visitingFunctionCall = false; return [txt]; }; From fd68ea98b0cb7464978d1b55878121b980d394a7 Mon Sep 17 00:00:00 2001 From: brianc Date: Mon, 14 Sep 2015 11:13:10 -0700 Subject: [PATCH 051/153] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 761075e7..d811eae5 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "author": "brianc ", "name": "sql", "description": "sql builder", - "version": "0.59.0", + "version": "0.60.0", "homepage": "https://github.com/brianc/node-sql", "license": "MIT", "repository": { From 0ee3d2cf1ac6e321322dc832a9f7377fff4aa7c4 Mon Sep 17 00:00:00 2001 From: Eric Perry Date: Tue, 27 Oct 2015 17:04:29 -0400 Subject: [PATCH 052/153] Added configurability for dialects --- lib/dialect/sqlite.js | 5 ++++- lib/index.js | 13 +++++++------ lib/node/index.js | 9 +++++++-- test/dialects/support.js | 8 ++++---- test/dialects/tostring-tests.js | 29 +++++++++++++++++++++++++++++ 5 files changed, 51 insertions(+), 13 deletions(-) diff --git a/lib/dialect/sqlite.js b/lib/dialect/sqlite.js index fdabf71a..611112e8 100644 --- a/lib/dialect/sqlite.js +++ b/lib/dialect/sqlite.js @@ -3,10 +3,11 @@ var util = require('util'); var assert = require('assert'); -var Sqlite = function() { +var Sqlite = function(config) { this.output = []; this.params = []; this._hasAddedAColumn = false; + this.config = config || {}; }; var Postgres = require(__dirname + '/postgres'); @@ -20,6 +21,8 @@ Sqlite.prototype._arrayAggFunctionName = 'GROUP_CONCAT'; 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 { value = Postgres.prototype._getParameterValue.call(this, value); } diff --git a/lib/index.js b/lib/index.js index 372de60d..6f609570 100644 --- a/lib/index.js +++ b/lib/index.js @@ -12,8 +12,8 @@ var Table = require('./table'); // 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(); @@ -50,19 +50,20 @@ Sql.prototype.select = function() { }; // 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; }; // 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/index.js b/lib/node/index.js index e0dabff5..b500ab39 100644 --- a/lib/node/index.js +++ b/lib/node/index.js @@ -45,9 +45,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 +66,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) { diff --git a/test/dialects/support.js b/test/dialects/support.js index e8c68837..f1d926e9 100644 --- a/test/dialects/support.js +++ b/test/dialects/support.js @@ -27,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; @@ -51,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); diff --git a/test/dialects/tostring-tests.js b/test/dialects/tostring-tests.js index 215cb05b..53d413e8 100644 --- a/test/dialects/tostring-tests.js +++ b/test/dialects/tostring-tests.js @@ -134,6 +134,35 @@ Harness.test({ params: [new Date('Sat, 01 Jan 2000 00:00:00 GMT')] }); +// Date to milliseconds +Harness.test({ + query: post.content.equals(new Date('Sat, 01 Jan 2000 00:00:00 GMT')), + pg: { + text : '("post"."content" = $1)', + string: '("post"."content" = \'2000-01-01T00:00:00.000Z\')' + }, + sqlite: { + text : '("post"."content" = $1)', + string: '("post"."content" = 946684800000)', + config: { + dateTimeMillis: true + } + }, + mysql: { + text : '(`post`.`content` = ?)', + string: '(`post`.`content` = \'2000-01-01T00:00:00.000Z\')' + }, + mssql: { + text : '([post].[content] = @1)', + string: '([post].[content] = \'2000-01-01T00:00:00.000Z\')' + }, + oracle: { + text : '("post"."content" = :1)', + string: '("post"."content" = \'2000-01-01T00:00:00.000Z\')' + }, + params: [new Date('Sat, 01 Jan 2000 00:00:00 GMT')] +}); + // Object var customObject = { toString: function() { From bf212de3422841e220acf2bd84c37511306c7b54 Mon Sep 17 00:00:00 2001 From: Alexey Zabrodsky Date: Thu, 29 Oct 2015 18:59:33 +0500 Subject: [PATCH 053/153] returning() defaults to '*' --- lib/node/query.js | 9 ++++++--- test/dialects/returning-tests.js | 20 ++++++++++++++++++++ 2 files changed, 26 insertions(+), 3 deletions(-) create mode 100644 test/dialects/returning-tests.js diff --git a/lib/node/query.js b/lib/node/query.js index 7b870aea..26bc7227 100644 --- a/lib/node/query.js +++ b/lib/node/query.js @@ -257,9 +257,12 @@ 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); }, @@ -462,7 +465,7 @@ var Query = Node.define({ }); return this.add(this.indexesClause); }, - + createView: function(viewName) { this.add(new CreateView(viewName)); return this; 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'] +}); From c4aacfbb272b02e5fb7dd8e44bfdcde0e02fd030 Mon Sep 17 00:00:00 2001 From: brianc Date: Mon, 2 Nov 2015 10:27:08 -0600 Subject: [PATCH 054/153] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d811eae5..f860204a 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "author": "brianc ", "name": "sql", "description": "sql builder", - "version": "0.60.0", + "version": "0.61.0", "homepage": "https://github.com/brianc/node-sql", "license": "MIT", "repository": { From 8deb5c5ff9a3a155ea62f593467b151b2b31d335 Mon Sep 17 00:00:00 2001 From: brianc Date: Mon, 2 Nov 2015 10:32:49 -0600 Subject: [PATCH 055/153] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f860204a..9912ac7a 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "author": "brianc ", "name": "sql", "description": "sql builder", - "version": "0.61.0", + "version": "0.62.0", "homepage": "https://github.com/brianc/node-sql", "license": "MIT", "repository": { From d98ce456dd35fe44116fdd44b3e18574888204eb Mon Sep 17 00:00:00 2001 From: Eric Perry Date: Fri, 13 Nov 2015 11:14:25 -0500 Subject: [PATCH 056/153] Added configuration for NULLS LAST in postgres --- lib/dialect/mssql.js | 3 +- lib/dialect/mysql.js | 3 +- lib/dialect/oracle.js | 19 ++++++------ lib/dialect/postgres.js | 14 +++++---- test/dialects/order-tests.js | 56 ++++++++++++++++++++++++++++++++++++ 5 files changed, 79 insertions(+), 16 deletions(-) diff --git a/lib/dialect/mssql.js b/lib/dialect/mssql.js index 1d5f30df..daf64938 100644 --- a/lib/dialect/mssql.js +++ b/lib/dialect/mssql.js @@ -6,9 +6,10 @@ var util = require('util'); var assert = require('assert'); -var Mssql = function() { +var Mssql = function(config) { this.output = []; this.params = []; + this.config = config || {}; }; var Postgres = require(__dirname + '/postgres'); diff --git a/lib/dialect/mysql.js b/lib/dialect/mysql.js index fc4c6cfc..b4e8249c 100644 --- a/lib/dialect/mysql.js +++ b/lib/dialect/mysql.js @@ -3,9 +3,10 @@ var util = require('util'); var assert = require('assert'); -var Mysql = function() { +var Mysql = function(config) { this.output = []; this.params = []; + this.config = config || {}; }; var Postgres = require(__dirname + '/postgres'); diff --git a/lib/dialect/oracle.js b/lib/dialect/oracle.js index 763b2c5a..ce912b2c 100644 --- a/lib/dialect/oracle.js +++ b/lib/dialect/oracle.js @@ -3,9 +3,10 @@ var util = require('util'); var assert = require('assert'); -var Oracle = function() { +var Oracle = function(config) { this.output = []; this.params = []; + this.config = config || {}; }; var Postgres = require(__dirname + '/postgres'); @@ -55,7 +56,7 @@ Oracle.prototype.visitDrop = function(drop) { } // Implement our own drop if exists: // PostgreSQL: DROP TABLE IF EXISTS "group" - // Oracle: + // Oracle: // BEGIN // EXECUTE IMMEDIATE 'DROP TABLE POST'; // EXCEPTION @@ -80,7 +81,7 @@ Oracle.prototype.visitCreate = function(create) { if (isNotExists) { // Implement our own create if not exists: // PostgreSQL: CREATE TABLE IF NOT EXISTS "group" ("id" varchar(100)) - // Oracle: + // Oracle: // BEGIN // EXECUTE IMMEDIATE 'CREATE TABLE ...'; // EXCEPTION @@ -128,7 +129,7 @@ Oracle.prototype.visitModifier = function(node) { 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'); @@ -154,7 +155,7 @@ Oracle.prototype.visitColumn = function(columnNode) { 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(*)' @@ -196,11 +197,11 @@ Oracle.prototype.visitIndexes = function(node) { 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; }; @@ -219,9 +220,9 @@ Oracle.prototype.visitDropIndex = function(node) { // Using same CASE implementation as MSSQL Oracle.prototype.visitCase = function(caseExp) { - + return Mssql.prototype.visitCase.call(this, caseExp); -} +} function isCreateIfNotExists(create){ diff --git a/lib/dialect/postgres.js b/lib/dialect/postgres.js index 90daf4a1..0dc5f97a 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; @@ -71,7 +72,7 @@ Postgres.prototype.getQuery = function(queryNode) { queryNode = queryNode.select(queryNode.star()); } this.output = this.visit(queryNode); - + //if is a create view, must replace paramaters with values if (this.output.indexOf('CREATE VIEW') > -1) { var previousFlagStatus = this._disableParameterPlaceholders; @@ -81,10 +82,10 @@ Postgres.prototype.getQuery = function(queryNode) { this.params = []; this._disableParameterPlaceholders = previousFlagStatus; } - + // create the query object var query = { text: this.output.join(' '), values: this.params }; - + // reset the internal state of this builder this.output = []; this.params = []; @@ -154,7 +155,7 @@ Postgres.prototype.visit = function(node) { case 'FUNCTION CALL' : return this.visitFunctionCall(node); case 'ARRAY CALL' : return this.visitArrayCall(node); case 'CREATE VIEW' : return this.visitCreateView(node); - + case 'POSTFIX UNARY' : return this.visitPostfixUnary(node); case 'PREFIX UNARY' : return this.visitPrefixUnary(node); case 'BINARY' : return this.visitBinary(node); @@ -371,6 +372,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.nullsLast) { + result.push('NULLS LAST'); + } return result; }; diff --git a/test/dialects/order-tests.js b/test/dialects/order-tests.js index 888f27f3..476d625e 100644 --- a/test/dialects/order-tests.js +++ b/test/dialects/order-tests.js @@ -253,3 +253,59 @@ Harness.test({ }, 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: { + nullsLast: true + } + }, + 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: { + nullsLast: true + } + }, + 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: [] +}); \ No newline at end of file From 3c150c00d38fe0eeb1d6955cc18c5696efc594a7 Mon Sep 17 00:00:00 2001 From: Eric Perry Date: Fri, 13 Nov 2015 11:19:42 -0500 Subject: [PATCH 057/153] Changed null order so either 'first' or 'last' can be specified --- lib/dialect/postgres.js | 4 ++-- test/dialects/order-tests.js | 32 ++++++++++++++++++++++++++++++-- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/lib/dialect/postgres.js b/lib/dialect/postgres.js index 0dc5f97a..c46a44d2 100644 --- a/lib/dialect/postgres.js +++ b/lib/dialect/postgres.js @@ -372,8 +372,8 @@ 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.nullsLast) { - result.push('NULLS LAST'); + if (this._myClass === Postgres && this.config.nullOrder) { + result.push('NULLS ' + this.config.nullOrder.toUpperCase()); } return result; }; diff --git a/test/dialects/order-tests.js b/test/dialects/order-tests.js index 476d625e..2c4803f5 100644 --- a/test/dialects/order-tests.js +++ b/test/dialects/order-tests.js @@ -260,7 +260,7 @@ Harness.test({ 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: { - nullsLast: true + nullOrder: "last" } }, sqlite: { @@ -288,7 +288,35 @@ Harness.test({ 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: { - nullsLast: true + 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: { From fb67977497f9f37a88c8da7b53fb4df79233f86c Mon Sep 17 00:00:00 2001 From: brianc Date: Fri, 13 Nov 2015 10:43:15 -0600 Subject: [PATCH 058/153] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9912ac7a..7bd6325f 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "author": "brianc ", "name": "sql", "description": "sql builder", - "version": "0.62.0", + "version": "0.63.0", "homepage": "https://github.com/brianc/node-sql", "license": "MIT", "repository": { From c6b0d101814d8fcb9cb2afaf4043399032656476 Mon Sep 17 00:00:00 2001 From: Barry Hammen Date: Fri, 13 Nov 2015 15:33:52 -0500 Subject: [PATCH 059/153] Add left-join support to subQuery --- lib/node/query.js | 6 ++++++ test/dialects/subquery-tests.js | 27 +++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/lib/node/query.js b/lib/node/query.js index 26bc7227..85cfa9e5 100644 --- a/lib/node/query.js +++ b/lib/node/query.js @@ -40,6 +40,7 @@ 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) { @@ -128,6 +129,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 diff --git a/test/dialects/subquery-tests.js b/test/dialects/subquery-tests.js index e27beecf..1458c6e1 100644 --- a/test/dialects/subquery-tests.js +++ b/test/dialects/subquery-tests.js @@ -162,3 +162,30 @@ Harness.test({ }, 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: [] +}); + From 8cb528094c3c91feb26afec6849c83a49d5895f4 Mon Sep 17 00:00:00 2001 From: Barry Hammen Date: Mon, 16 Nov 2015 10:29:36 -0500 Subject: [PATCH 060/153] Fixed passing config to subquery --- lib/dialect/postgres.js | 2 +- test/dialects/subquery-tests.js | 30 ++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/lib/dialect/postgres.js b/lib/dialect/postgres.js index c46a44d2..deae9716 100644 --- a/lib/dialect/postgres.js +++ b/lib/dialect/postgres.js @@ -662,7 +662,7 @@ Postgres.prototype.visitQueryHelper=function(actions,targets,filters){ Postgres.prototype.visitSubquery = function(queryNode) { // 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; diff --git a/test/dialects/subquery-tests.js b/test/dialects/subquery-tests.js index e27beecf..dd2f27e8 100644 --- a/test/dialects/subquery-tests.js +++ b/test/dialects/subquery-tests.js @@ -85,6 +85,36 @@ Harness.test({ 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: { From ea5cf415e8252f73b9ffa1b667e79399772df5f4 Mon Sep 17 00:00:00 2001 From: brianc Date: Tue, 17 Nov 2015 10:01:50 -0700 Subject: [PATCH 061/153] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7bd6325f..bf9f0476 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "author": "brianc ", "name": "sql", "description": "sql builder", - "version": "0.63.0", + "version": "0.63.1", "homepage": "https://github.com/brianc/node-sql", "license": "MIT", "repository": { From 8012e59e977938394c9565ce0d2a52d8a98bbc2d Mon Sep 17 00:00:00 2001 From: iamcharliegoddard Date: Fri, 11 Dec 2015 23:31:59 -0800 Subject: [PATCH 062/153] updated value exists to type check --- lib/dialect/postgres.js | 2 +- test/column-tests.js | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/dialect/postgres.js b/lib/dialect/postgres.js index deae9716..35cc0fae 100644 --- a/lib/dialect/postgres.js +++ b/lib/dialect/postgres.js @@ -729,7 +729,7 @@ Postgres.prototype.visitColumn = function(columnNode) { } } if(!inInsertUpdateClause && !this.visitingReturning && !this._visitingCreate && !this._visitingAlter && !columnNode.subfieldContainer) { - if(table.alias) { + if(typeof table.alias === 'string') { txt.push(this.quote(table.alias)); } else { if(table.getSchema()) { diff --git a/test/column-tests.js b/test/column-tests.js index 9bcc15ac..6fb61559 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"'); }); From aa98ea9ae8137475cf3c7ff7e41f146f4bc7f245 Mon Sep 17 00:00:00 2001 From: iamcharliegoddard Date: Mon, 14 Dec 2015 13:07:34 -0800 Subject: [PATCH 063/153] additional check of alias as string --- lib/dialect/postgres.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/dialect/postgres.js b/lib/dialect/postgres.js index 35cc0fae..2c65f6f5 100644 --- a/lib/dialect/postgres.js +++ b/lib/dialect/postgres.js @@ -689,7 +689,7 @@ Postgres.prototype.visitTable = function(tableNode) { txt += '.'; } txt += this.quote(table.getName()); - if(table.alias) { + if(typeof table.alias === 'string') { txt += this._aliasText + this.quote(table.alias); } return [txt]; From 6b4ec4a91294e94a8b986cdd01e1a0a69b2a7aa3 Mon Sep 17 00:00:00 2001 From: brianc Date: Fri, 18 Dec 2015 23:02:16 -0600 Subject: [PATCH 064/153] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index bf9f0476..abed9bab 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "author": "brianc ", "name": "sql", "description": "sql builder", - "version": "0.63.1", + "version": "0.64.0", "homepage": "https://github.com/brianc/node-sql", "license": "MIT", "repository": { From 96b0b771ebde964f2fd0d652772d6748c5d9d4db Mon Sep 17 00:00:00 2001 From: brianc Date: Fri, 18 Dec 2015 23:37:13 -0600 Subject: [PATCH 065/153] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index abed9bab..57213252 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "author": "brianc ", "name": "sql", "description": "sql builder", - "version": "0.64.0", + "version": "0.64.1", "homepage": "https://github.com/brianc/node-sql", "license": "MIT", "repository": { From da3975cf67251b6dd180ee02361be59016598698 Mon Sep 17 00:00:00 2001 From: beedi Date: Wed, 30 Dec 2015 22:21:34 +0100 Subject: [PATCH 066/153] Added Tests for specific constraint on references column, e.g. DEFERRABLE --- test/dialects/create-table-tests.js | 79 +++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/test/dialects/create-table-tests.js b/test/dialects/create-table-tests.js index f9c3e2fa..798a077d 100644 --- a/test/dialects/create-table-tests.js +++ b/test/dialects/create-table-tests.js @@ -461,3 +461,82 @@ Harness.test({ params: [] }); +const 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: [] +}); + +const 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: [] +}); \ No newline at end of file From 642592efaa1c1a5738911b353e7ea81dca0af78e Mon Sep 17 00:00:00 2001 From: beedi Date: Wed, 30 Dec 2015 22:37:06 +0100 Subject: [PATCH 067/153] Fix typo in test --- test/dialects/create-table-tests.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/dialects/create-table-tests.js b/test/dialects/create-table-tests.js index 798a077d..160e7262 100644 --- a/test/dialects/create-table-tests.js +++ b/test/dialects/create-table-tests.js @@ -461,7 +461,7 @@ Harness.test({ params: [] }); -const users = Table.define({ +var users = Table.define({ name: 'users', columns: { id: { @@ -501,7 +501,7 @@ Harness.test({ params: [] }); -const noUsers = Table.define({ +var noUsers = Table.define({ name: 'no_users', columns: { id: { From f41c5f53adad06480aca59af878179c2a5606c78 Mon Sep 17 00:00:00 2001 From: Dan Rzeppa Date: Sat, 2 Jan 2016 08:45:18 -0500 Subject: [PATCH 068/153] Adds ability to override the default parameter place holder of @index and use ? instead. Addresses #285. --- lib/dialect/mssql.js | 9 +++++++++ test/index-tests.js | 16 ++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/lib/dialect/mssql.js b/lib/dialect/mssql.js index daf64938..2cb73b40 100644 --- a/lib/dialect/mssql.js +++ b/lib/dialect/mssql.js @@ -6,6 +6,14 @@ var util = require('util'); var assert = require('assert'); +/** + * Config can contain: + * + * questionMarkParameterPlaceholder:true which will use a "?" for the parameter placeholder instead of the @index. + * + * @param config + * @constructor + */ var Mssql = function(config) { this.output = []; this.params = []; @@ -23,6 +31,7 @@ Mssql.prototype._quoteCharacter = '['; Mssql.prototype._arrayAggFunctionName = ''; Mssql.prototype._getParameterPlaceholder = function(index, value) { + if (this.config.questionMarkParameterPlaceholder) return '?'; return '@' + index; }; diff --git a/test/index-tests.js b/test/index-tests.js index 0893a7ef..f25839e1 100644 --- a/test/index-tests.js +++ b/test/index-tests.js @@ -195,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'); + }); + }); From bc2c205557b45e02a995cc80294a0c1a18ea32b3 Mon Sep 17 00:00:00 2001 From: iamcharliegoddard Date: Sun, 3 Jan 2016 01:00:15 -0800 Subject: [PATCH 069/153] Issue-266: on create added UNIQUE column constraint --- lib/dialect/postgres.js | 3 + lib/node/column.js | 1 + test/dialects/create-table-tests.js | 102 ++++++++++++++++++++++++++++ 3 files changed, 106 insertions(+) diff --git a/lib/dialect/postgres.js b/lib/dialect/postgres.js index 2c65f6f5..c4b9eae8 100644 --- a/lib/dialect/postgres.js +++ b/lib/dialect/postgres.js @@ -785,6 +785,9 @@ Postgres.prototype.visitColumn = function(columnNode) { } else if (columnNode.notNull) { txt.push(' NOT NULL'); } + if (!columnNode.primaryKey && columnNode.unique) { + txt.push(' UNIQUE') + } } if (!!columnNode.references) { diff --git a/lib/node/column.js b/lib/node/column.js index 03b58ad5..6fa999fb 100644 --- a/lib/node/column.js +++ b/lib/node/column.js @@ -24,6 +24,7 @@ module.exports = Node.define({ 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/test/dialects/create-table-tests.js b/test/dialects/create-table-tests.js index f9c3e2fa..80d65fbc 100644 --- a/test/dialects/create-table-tests.js +++ b/test/dialects/create-table-tests.js @@ -461,3 +461,105 @@ Harness.test({ 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: [] +}); + +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: [] +}); From 3b850e85ee75119a3bdd79b5dcd96a4c12601bba Mon Sep 17 00:00:00 2001 From: brianc Date: Mon, 4 Jan 2016 15:03:51 -0600 Subject: [PATCH 070/153] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 57213252..c99ef1ea 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "author": "brianc ", "name": "sql", "description": "sql builder", - "version": "0.64.1", + "version": "0.65.0", "homepage": "https://github.com/brianc/node-sql", "license": "MIT", "repository": { From 93afffea186c451f47525c6f22ef6732a7b174eb Mon Sep 17 00:00:00 2001 From: iamcharliegoddard Date: Tue, 5 Jan 2016 00:34:31 -0800 Subject: [PATCH 071/153] jshint: missing semicolon --- lib/dialect/mssql.js | 26 +++++++++++++------------- lib/dialect/mysql.js | 2 +- lib/dialect/oracle.js | 12 ++++++------ lib/dialect/postgres.js | 28 ++++++++++++++-------------- lib/dialect/sqlite.js | 20 ++++++++++---------- lib/node/valueExpression.js | 4 ++-- lib/table.js | 6 +++--- runtests.js | 24 ++++++++++++------------ test/column-tests.js | 4 ++-- test/dialects/subquery-tests.js | 2 +- 10 files changed, 64 insertions(+), 64 deletions(-) diff --git a/lib/dialect/mssql.js b/lib/dialect/mssql.js index 2cb73b40..c5673371 100644 --- a/lib/dialect/mssql.js +++ b/lib/dialect/mssql.js @@ -104,7 +104,7 @@ 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: @@ -119,7 +119,7 @@ Mssql.prototype.visitAlter = function(alter) { "'COLUMN'" ]; self._visitingAlter = false; - return result + return result; } if (isAlterAddColumn(alter)) return _addColumn(); @@ -133,7 +133,7 @@ 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); @@ -163,7 +163,7 @@ Mssql.prototype.visitCase = function(caseExp) { text += ' END)'; return [text]; -} +}; Mssql.prototype.visitColumn = function(columnNode) { var self=this; @@ -171,12 +171,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); } @@ -193,8 +193,8 @@ Mssql.prototype.visitColumn = function(columnNode) { Mssql.prototype.visitCreate = function(create) { - var isNotExists=isCreateIfNotExists(create) - var isTemporary=isCreateTemporary(create) + var isNotExists=isCreateIfNotExists(create); + var isTemporary=isCreateTemporary(create); if (!isNotExists && !isTemporary) { return Mssql.super_.prototype.visitCreate.call(this, create); } @@ -219,7 +219,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 + if (!isNotExists) return createResult; return ['IF NOT EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES '+whereClause+') BEGIN '+createResult.join(' ')+' END']; }; @@ -246,9 +246,9 @@ Mssql.prototype.visitDrop = function(drop) { Mssql.prototype.visitFunctionCall = function(functionCall) { this._visitingFunctionCall = true; - var name=functionCall.name + var name=functionCall.name; // override the LENGTH function since mssql calls it LEN - if (name=="LENGTH") name="LEN" + if (name=="LENGTH") name="LEN"; var txt = name + '(' + functionCall.nodes.map(this.visit.bind(this)).join(', ') + ')'; this._visitingFunctionCall = false; return [txt]; @@ -385,7 +385,7 @@ Mssql.prototype.visitSelect = function(select) { // Node is either an OFFSET or LIMIT node function getModifierValue(dialect,node){ - return node.count.type ? dialect.visit(node.count) : node.count + return node.count.type ? dialect.visit(node.count) : node.count; } function isAlterAddColumn(alter){ @@ -426,7 +426,7 @@ function isCreateIfNotExists(create){ }; function isCreateTemporary(create){ - return create.options.isTemporary + return create.options.isTemporary; }; function isDropIfExists(drop){ diff --git a/lib/dialect/mysql.js b/lib/dialect/mysql.js index b4e8249c..9d582ce7 100644 --- a/lib/dialect/mysql.js +++ b/lib/dialect/mysql.js @@ -100,6 +100,6 @@ Mysql.prototype.visitBinary = function(binary) { return [text]; } return Mysql.super_.prototype.visitBinary.call(this, binary); -} +}; module.exports = Mysql; diff --git a/lib/dialect/oracle.js b/lib/dialect/oracle.js index ce912b2c..de52342e 100644 --- a/lib/dialect/oracle.js +++ b/lib/dialect/oracle.js @@ -75,7 +75,7 @@ Oracle.prototype.visitDrop = function(drop) { }; Oracle.prototype.visitCreate = function(create) { - var isNotExists=isCreateIfNotExists(create) + var isNotExists=isCreateIfNotExists(create); //var isTemporary=isCreateTemporary(create) var createText = Oracle.super_.prototype.visitCreate.call(this, create); if (isNotExists) { @@ -145,7 +145,7 @@ Oracle.prototype.visitQueryHelper=function(actions,targets,filters){ } return this.output; -} +}; Oracle.prototype.visitColumn = function(columnNode) { var self=this; @@ -153,12 +153,12 @@ Oracle.prototype.visitColumn = function(columnNode) { var inSelectClause; function _arrayAgg(){ - throw new Error("Oracle does not support array_agg.") + throw new Error("Oracle does not support array_agg."); } function _countStar(){ // Implement our own since count(table.*) is invalid in Oracle - var result='COUNT(*)' + var result='COUNT(*)'; if(inSelectClause && columnNode.alias) { result += self._aliasText + self.quote(columnNode.alias); } @@ -222,7 +222,7 @@ Oracle.prototype.visitDropIndex = function(node) { Oracle.prototype.visitCase = function(caseExp) { return Mssql.prototype.visitCase.call(this, caseExp); -} +}; function isCreateIfNotExists(create){ @@ -232,7 +232,7 @@ function isCreateIfNotExists(create){ }; function isCreateTemporary(create){ - return create.options.isTemporary + return create.options.isTemporary; }; function isDropIfExists(drop){ diff --git a/lib/dialect/postgres.js b/lib/dialect/postgres.js index c4b9eae8..a5f0f413 100644 --- a/lib/dialect/postgres.js +++ b/lib/dialect/postgres.js @@ -187,14 +187,14 @@ 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'] + var result = ['SELECT']; if (select.isDistinct) result.push('DISTINCT'); @@ -281,7 +281,7 @@ Postgres.prototype.visitCreate = function(create) { var col_nodes = table.columns.map(function(col) { return col.toNode(); }); var result = ['CREATE TABLE']; - if (create.options.isTemporary) result=['CREATE TEMPORARY 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) { @@ -547,7 +547,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) + ']'; @@ -637,7 +637,7 @@ Postgres.prototype.visitQuery = function(queryNode) { throw new Error('Create View requires a Select.'); } } - return this.visitQueryHelper(actions,targets,filters) + return this.visitQueryHelper(actions,targets,filters); }; /** @@ -649,7 +649,7 @@ Postgres.prototype.visitQuery = function(queryNode) { * @returns {String[]} */ Postgres.prototype.visitQueryHelper=function(actions,targets,filters){ - this.handleDistinct(actions, filters) + this.handleDistinct(actions, filters); // lazy-man sorting var sortedNodes = actions.concat(targets).concat(filters); for(var i = 0; i < sortedNodes.length; i++) { @@ -658,7 +658,7 @@ Postgres.prototype.visitQueryHelper=function(actions,targets,filters){ } // implicit 'from' return this.output; -} +}; Postgres.prototype.visitSubquery = function(queryNode) { // create another query builder of the current class to build the subquery @@ -786,7 +786,7 @@ Postgres.prototype.visitColumn = function(columnNode) { txt.push(' NOT NULL'); } if (!columnNode.primaryKey && columnNode.unique) { - txt.push(' UNIQUE') + txt.push(' UNIQUE'); } } @@ -817,7 +817,7 @@ Postgres.prototype.visitColumn = function(columnNode) { var onDelete = columnNode.references.onDelete; if (onDelete) onDelete = onDelete.toUpperCase(); if (onDelete === 'CASCADE' || onDelete === 'RESTRICT') { - txt.push(' ON DELETE ' + onDelete) + txt.push(' ON DELETE ' + onDelete); } } } @@ -1006,8 +1006,8 @@ Postgres.prototype.findNode=function(list,type) { var n=list[i]; if (n.type==type) return {index:i,node:n}; } - return undefined -} + return undefined; +}; /** * pulls the DISTINCT node out of the filters and flags the SELECT node that it should be distinct. @@ -1017,11 +1017,11 @@ Postgres.prototype.handleDistinct = function(actions,filters) { var distinctNode = this.findNode(filters,"DISTINCT"); //if (!distinctNode) distinctNode = _findNode(targets,"DISTINCT"); //if (!distinctNode) distinctNode = _findNode(actions,"DISTINCT"); - if (!distinctNode) return + if (!distinctNode) return; var selectInfo = this.findNode(actions,"SELECT"); - if (!selectInfo) return // there should be one by now, I think + if (!selectInfo) return; // there should be one by now, I think // mark the SELECT node that it's distinct selectInfo.node.isDistinct = true; -} +}; module.exports = Postgres; diff --git a/lib/dialect/sqlite.js b/lib/dialect/sqlite.js index 611112e8..6f487719 100644 --- a/lib/dialect/sqlite.js +++ b/lib/dialect/sqlite.js @@ -38,28 +38,28 @@ Sqlite.prototype.visitDropColumn = function() { }; Sqlite.prototype.visitFunctionCall = function(functionCall) { - var _this=this + 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 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 + 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 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 + return txt; } - var txt="" - var name=functionCall.name + 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(); @@ -123,6 +123,6 @@ Sqlite.prototype.visitBinary = function(binary) { return ret; } return Sqlite.super_.prototype.visitBinary.call(this, binary); -} +}; module.exports = Sqlite; diff --git a/lib/node/valueExpression.js b/lib/node/valueExpression.js index e04ba5ff..34f6e896 100644 --- a/lib/node/valueExpression.js +++ b/lib/node/valueExpression.js @@ -110,8 +110,8 @@ var ValueExpressionMixin = function() { whenList : processParams(whenList), thenList : processParams(thenList), else : elseBranch - }) - } + }); + }; return { isNull : postfixUnaryMethod('IS NULL'), diff --git a/lib/table.js b/lib/table.js index f9c0b88c..c702a6e3 100644 --- a/lib/table.js +++ b/lib/table.js @@ -15,7 +15,7 @@ var Table = function(config) { this._name = config.name; this._initialConfig = config; this.columnWhiteList = !!config.columnWhiteList; - this.isTemporary=!!config.isTemporary + this.isTemporary=!!config.isTemporary; this.snakeToCamel = !!config.snakeToCamel; this.columns = []; this.table = this; @@ -76,10 +76,10 @@ Table.prototype.createColumn = function(col) { table: this, subfieldContainer: col, name: subfield - })] + })]; }, this)) .object() - .value() + .value(); } } 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/column-tests.js b/test/column-tests.js index 6fb61559..f1a6f1a5 100644 --- a/test/column-tests.js +++ b/test/column-tests.js @@ -96,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/subquery-tests.js b/test/dialects/subquery-tests.js index 1c79c283..fe5b2aad 100644 --- a/test/dialects/subquery-tests.js +++ b/test/dialects/subquery-tests.js @@ -29,7 +29,7 @@ Harness.test({ string: 'SELECT "user"."name" FROM "user" WHERE ("user"."id" IN (SELECT "post"."userId" FROM "post"))' }, params: [] -}) +}); Harness.test({ query: user.name.in( From 66f2ce0b4e89c675554b8917dcc8faf719b5bc59 Mon Sep 17 00:00:00 2001 From: iamcharliegoddard Date: Tue, 5 Jan 2016 00:39:42 -0800 Subject: [PATCH 072/153] jshint: Unnecessary semicolon --- lib/dialect/mssql.js | 18 +++++++++--------- lib/dialect/oracle.js | 10 +++++----- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/lib/dialect/mssql.js b/lib/dialect/mssql.js index c5673371..2845ce42 100644 --- a/lib/dialect/mssql.js +++ b/lib/dialect/mssql.js @@ -392,52 +392,52 @@ function isAlterAddColumn(alter){ if (alter.nodes.length==0) return false; if (alter.nodes[0].type!='ADD COLUMN') return false; return true; -}; +} function isAlterDropColumn(alter){ if (alter.nodes.length==0) return false; if (alter.nodes[0].type!='DROP COLUMN') return false; return true; -}; +} function isAlterRename(alter){ if (alter.nodes.length==0) return false; if (alter.nodes[0].type!='RENAME') return false; return true; -}; +} function isAlterRenameColumn(alter){ if (alter.nodes.length==0) return false; if (alter.nodes[0].type!='RENAME COLUMN') return false; return true; -}; +} function isCountStarExpression(columnNode){ if (!columnNode.aggregator) return false; if (columnNode.aggregator.toLowerCase()!='count') return false; if (!columnNode.star) return false; return true; -}; +} function isCreateIfNotExists(create){ if (create.nodes.length==0) return false; if (create.nodes[0].type!='IF NOT EXISTS') return false; return true; -}; +} function isCreateTemporary(create){ return create.options.isTemporary; -}; +} function isDropIfExists(drop){ if (drop.nodes.length==0) return false; if (drop.nodes[0].type!='IF EXISTS') return false; return true; -}; +} // SQL Server does not support array expressions except in the IN clause. function isRightSideArray(binary){ return Array.isArray(binary.right); -}; +} module.exports = Mssql; diff --git a/lib/dialect/oracle.js b/lib/dialect/oracle.js index de52342e..f1e3567e 100644 --- a/lib/dialect/oracle.js +++ b/lib/dialect/oracle.js @@ -229,28 +229,28 @@ 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; -}; +} module.exports = Oracle; From 43c1ceee0f00bd7abc54e7464b5d9ea8e869e217 Mon Sep 17 00:00:00 2001 From: iamcharliegoddard Date: Tue, 5 Jan 2016 00:45:53 -0800 Subject: [PATCH 073/153] jshint: Use '===' to compare with '0' --- lib/dialect/mssql.js | 12 ++++++------ lib/dialect/oracle.js | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/dialect/mssql.js b/lib/dialect/mssql.js index 2845ce42..ca1286af 100644 --- a/lib/dialect/mssql.js +++ b/lib/dialect/mssql.js @@ -389,25 +389,25 @@ function getModifierValue(dialect,node){ } function isAlterAddColumn(alter){ - if (alter.nodes.length==0) return false; + 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.length===0) return false; if (alter.nodes[0].type!='DROP COLUMN') return false; return true; } function isAlterRename(alter){ - if (alter.nodes.length==0) return false; + if (alter.nodes.length===0) return false; if (alter.nodes[0].type!='RENAME') return false; return true; } function isAlterRenameColumn(alter){ - if (alter.nodes.length==0) return false; + if (alter.nodes.length===0) return false; if (alter.nodes[0].type!='RENAME COLUMN') return false; return true; } @@ -420,7 +420,7 @@ function isCountStarExpression(columnNode){ } function isCreateIfNotExists(create){ - if (create.nodes.length==0) return false; + if (create.nodes.length===0) return false; if (create.nodes[0].type!='IF NOT EXISTS') return false; return true; } @@ -430,7 +430,7 @@ function isCreateTemporary(create){ } function isDropIfExists(drop){ - if (drop.nodes.length==0) return false; + if (drop.nodes.length===0) return false; if (drop.nodes[0].type!='IF EXISTS') return false; return true; } diff --git a/lib/dialect/oracle.js b/lib/dialect/oracle.js index f1e3567e..54343717 100644 --- a/lib/dialect/oracle.js +++ b/lib/dialect/oracle.js @@ -226,7 +226,7 @@ Oracle.prototype.visitCase = function(caseExp) { function isCreateIfNotExists(create){ - if (create.nodes.length==0) return false; + if (create.nodes.length===0) return false; if (create.nodes[0].type!='IF NOT EXISTS') return false; return true; } @@ -236,7 +236,7 @@ function isCreateTemporary(create){ } function isDropIfExists(drop){ - if (drop.nodes.length==0) return false; + if (drop.nodes.length===0) return false; if (drop.nodes[0].type!='IF EXISTS') return false; return true; } From 2fdd98404f46a598fcfff6db7a5d52d410f5ba59 Mon Sep 17 00:00:00 2001 From: iamcharliegoddard Date: Tue, 5 Jan 2016 00:56:07 -0800 Subject: [PATCH 074/153] jhint: Use '===' to compare with 'true' --- lib/dialect/mssql.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/dialect/mssql.js b/lib/dialect/mssql.js index ca1286af..8f11150f 100644 --- a/lib/dialect/mssql.js +++ b/lib/dialect/mssql.js @@ -139,7 +139,7 @@ Mssql.prototype.visitCase = function(caseExp) { 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); From 31f388ae612ff271cbc4a2e38a092fd6a3913bca Mon Sep 17 00:00:00 2001 From: iamcharliegoddard Date: Tue, 5 Jan 2016 01:00:07 -0800 Subject: [PATCH 075/153] jshit: Use '!==' to compare with 'null' --- lib/dialect/mssql.js | 2 +- lib/dialect/postgres.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/dialect/mssql.js b/lib/dialect/mssql.js index 8f11150f..84c0b839 100644 --- a/lib/dialect/mssql.js +++ b/lib/dialect/mssql.js @@ -155,7 +155,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); } diff --git a/lib/dialect/postgres.js b/lib/dialect/postgres.js index a5f0f413..a0683686 100644 --- a/lib/dialect/postgres.js +++ b/lib/dialect/postgres.js @@ -539,7 +539,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); } From 930ec220c78d7688f19fc6cd7a97da4a49044e67 Mon Sep 17 00:00:00 2001 From: iamcharliegoddard Date: Tue, 5 Jan 2016 01:04:17 -0800 Subject: [PATCH 076/153] jshint: Use '!==' to compare with 'undefined' --- lib/dialect/mssql.js | 4 ++-- lib/dialect/postgres.js | 2 +- lib/node/valueExpression.js | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/dialect/mssql.js b/lib/dialect/mssql.js index 84c0b839..7b0d52d3 100644 --- a/lib/dialect/mssql.js +++ b/lib/dialect/mssql.js @@ -155,7 +155,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); } @@ -302,7 +302,7 @@ Mssql.prototype.visitQueryHelper=function(actions,targets,filters){ */ function _processLimit(limitInfo){ var selectInfo=Mssql.super_.prototype.findNode.call(this, actions, "SELECT"); - assert(selectInfo!=undefined,"MS SQL Server requires a SELECT clause when using LIMIT"); + assert(selectInfo!==undefined,"MS SQL Server requires a SELECT clause when using LIMIT"); // save the LIMIT node with the SELECT node selectInfo.node.msSQLLimitNode=limitInfo.node; // remove the LIMIT node from the filters so it doesn't get processed later. diff --git a/lib/dialect/postgres.js b/lib/dialect/postgres.js index a0683686..23f5a236 100644 --- a/lib/dialect/postgres.js +++ b/lib/dialect/postgres.js @@ -539,7 +539,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); } diff --git a/lib/node/valueExpression.js b/lib/node/valueExpression.js index 34f6e896..9be50e7a 100644 --- a/lib/node/valueExpression.js +++ b/lib/node/valueExpression.js @@ -103,7 +103,7 @@ var ValueExpressionMixin = function() { }; var caseMethod = function(whenList, thenList, elseBranch) { - if (undefined != elseBranch) { + if (undefined !== elseBranch) { elseBranch = processParams(elseBranch); } return new CaseNode({ From 0aceecbcc1cfbd9b57a9b3246faea122900d11fe Mon Sep 17 00:00:00 2001 From: iamcharliegoddard Date: Tue, 5 Jan 2016 01:06:27 -0800 Subject: [PATCH 077/153] jshint: Use '===' to compare with '0' --- lib/table.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/table.js b/lib/table.js index c702a6e3..86e8adbe 100644 --- a/lib/table.js +++ b/lib/table.js @@ -195,7 +195,7 @@ Table.prototype.subQuery = function(alias) { Table.prototype.insert = function() { var query = new Query(this); - if(arguments[0].length == 0){ + if(arguments[0].length === 0){ query.select.call(query, this.star()); query.where.apply(query,["1=2"]); } else { From f2016acc24339a06b322e89b2a368701bd24fb57 Mon Sep 17 00:00:00 2001 From: iamcharliegoddard Date: Tue, 5 Jan 2016 01:18:36 -0800 Subject: [PATCH 078/153] jshint: unused variables --- lib/node/valueExpression.js | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/lib/node/valueExpression.js b/lib/node/valueExpression.js index 9be50e7a..faad8033 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); }; From 888c4e5681df66785999932e3fedc4a7536fab0b Mon Sep 17 00:00:00 2001 From: iamcharliegoddard Date: Tue, 5 Jan 2016 01:36:00 -0800 Subject: [PATCH 079/153] jshint: ignore:line --- lib/dialect/postgres.js | 2 +- lib/node/query.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/dialect/postgres.js b/lib/dialect/postgres.js index 23f5a236..74e3769a 100644 --- a/lib/dialect/postgres.js +++ b/lib/dialect/postgres.js @@ -594,7 +594,7 @@ Postgres.prototype.visitQuery = function(queryNode) { var node = queryNode.nodes[i]; switch(node.type) { case "SELECT": - isSelect = true; + isSelect = true; // jshint ignore:line case "DELETE": actions.push(node); break; diff --git a/lib/node/query.js b/lib/node/query.js index 85cfa9e5..0e05985f 100644 --- a/lib/node/query.js +++ b/lib/node/query.js @@ -280,7 +280,7 @@ var Query = Node.define({ var col = self.table.get(key); if(col && !col.autoGenerated) var val = o[key]; - onDuplicate.add(col.value(ParameterNode.getNodeOrParameterNode(val))); + onDuplicate.add(col.value(ParameterNode.getNodeOrParameterNode(val))); // jshint ignore:line }); return self.add(onDuplicate); From e0643275c9c5dd1a2b48a7ed58fb069283cbd6c1 Mon Sep 17 00:00:00 2001 From: iamcharliegoddard Date: Tue, 5 Jan 2016 01:39:40 -0800 Subject: [PATCH 080/153] jshint: Possible strict violation --- lib/dialect/mssql.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/dialect/mssql.js b/lib/dialect/mssql.js index 7b0d52d3..5c547f49 100644 --- a/lib/dialect/mssql.js +++ b/lib/dialect/mssql.js @@ -282,9 +282,9 @@ Mssql.prototype.visitOrderBy = function(orderBy) { */ Mssql.prototype.visitQueryHelper=function(actions,targets,filters){ function _handleLimitAndOffset(){ - var limitInfo=Mssql.super_.prototype.findNode.call(this, filters, "LIMIT"); - var offsetInfo=Mssql.super_.prototype.findNode.call(this, filters, "OFFSET"); - var orderByInfo=Mssql.super_.prototype.findNode.call(this, filters, "ORDER BY"); + var limitInfo=Mssql.super_.prototype.findNode.call(this, filters, "LIMIT"); // jshint ignore:line + var offsetInfo=Mssql.super_.prototype.findNode.call(this, filters, "OFFSET"); // jshint ignore:line + var orderByInfo=Mssql.super_.prototype.findNode.call(this, filters, "ORDER BY"); // jshint ignore:line // no OFFSET or LIMIT then there's nothing special to do if (!offsetInfo && !limitInfo) return; @@ -301,7 +301,7 @@ Mssql.prototype.visitQueryHelper=function(actions,targets,filters){ * @private */ function _processLimit(limitInfo){ - var selectInfo=Mssql.super_.prototype.findNode.call(this, actions, "SELECT"); + var selectInfo=Mssql.super_.prototype.findNode.call(this, actions, "SELECT"); // jshint ignore:line assert(selectInfo!==undefined,"MS SQL Server requires a SELECT clause when using LIMIT"); // save the LIMIT node with the SELECT node selectInfo.node.msSQLLimitNode=limitInfo.node; From 1c55b0ecdadf4e131e27437367632893295a4dda Mon Sep 17 00:00:00 2001 From: iamcharliegoddard Date: Tue, 5 Jan 2016 01:43:22 -0800 Subject: [PATCH 081/153] jshint: 'i' is already defined --- lib/dialect/postgres.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/dialect/postgres.js b/lib/dialect/postgres.js index 74e3769a..6af424d9 100644 --- a/lib/dialect/postgres.js +++ b/lib/dialect/postgres.js @@ -766,7 +766,7 @@ Postgres.prototype.visitColumn = function(columnNode) { txt.push(this.quote(columnNode.name)); } if(closeParen) { - for(var i = 0; i < closeParen; i++) { + for(var j = 0; j < closeParen; j++) { txt.push(')'); } } From b6e7e7140160be1a1d81179705ec5c7a6ab6f22c Mon Sep 17 00:00:00 2001 From: iamcharliegoddard Date: Tue, 5 Jan 2016 01:49:29 -0800 Subject: [PATCH 082/153] jshint: Bad line breaking before '&&' --- lib/dialect/postgres.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/dialect/postgres.js b/lib/dialect/postgres.js index 6af424d9..764289b5 100644 --- a/lib/dialect/postgres.js +++ b/lib/dialect/postgres.js @@ -702,11 +702,11 @@ Postgres.prototype.visitColumn = function(columnNode) { 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; From c7a36a39cfb769fd95427bc9e070416e034b904b Mon Sep 17 00:00:00 2001 From: iamcharliegoddard Date: Tue, 5 Jan 2016 09:44:31 -0800 Subject: [PATCH 083/153] jshint: tabs to spaces --- lib/dialect/mssql.js | 154 ++++++++++++++++++++-------------------- lib/dialect/oracle.js | 2 +- lib/dialect/postgres.js | 44 ++++++------ lib/dialect/sqlite.js | 46 ++++++------ lib/node/drop.js | 4 +- lib/node/parameter.js | 2 +- lib/node/query.js | 8 +-- lib/node/truncate.js | 4 +- test/column-tests.js | 46 ++++++------ 9 files changed, 155 insertions(+), 155 deletions(-) diff --git a/lib/dialect/mssql.js b/lib/dialect/mssql.js index 5c547f49..48c1ad45 100644 --- a/lib/dialect/mssql.js +++ b/lib/dialect/mssql.js @@ -31,7 +31,7 @@ Mssql.prototype._quoteCharacter = '['; Mssql.prototype._arrayAggFunctionName = ''; Mssql.prototype._getParameterPlaceholder = function(index, value) { - if (this.config.questionMarkParameterPlaceholder) return '?'; + if (this.config.questionMarkParameterPlaceholder) return '?'; return '@' + index; }; @@ -246,24 +246,24 @@ Mssql.prototype.visitDrop = function(drop) { Mssql.prototype.visitFunctionCall = function(functionCall) { this._visitingFunctionCall = true; - var name=functionCall.name; - // override the LENGTH function since mssql calls it LEN - if (name=="LENGTH") name="LEN"; + var name=functionCall.name; + // override the LENGTH function since mssql calls it LEN + if (name=="LENGTH") name="LEN"; var txt = name + '(' + functionCall.nodes.map(this.visit.bind(this)).join(', ') + ')'; this._visitingFunctionCall = false; return [txt]; }; 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; }; /** @@ -281,63 +281,63 @@ Mssql.prototype.visitOrderBy = function(orderBy) { * @returns {String[]} */ Mssql.prototype.visitQueryHelper=function(actions,targets,filters){ - function _handleLimitAndOffset(){ - var limitInfo=Mssql.super_.prototype.findNode.call(this, filters, "LIMIT"); // jshint ignore:line - var offsetInfo=Mssql.super_.prototype.findNode.call(this, filters, "OFFSET"); // jshint ignore:line - var orderByInfo=Mssql.super_.prototype.findNode.call(this, filters, "ORDER BY"); // jshint ignore:line - - // no OFFSET or LIMIT then there's nothing special to do - if (!offsetInfo && !limitInfo) return; - // ORDER BY with OFFSET we have work to do, may consume LIMIT as well - if (orderByInfo && offsetInfo) _processOrderByOffsetLimit(orderByInfo,offsetInfo,limitInfo); - else if (offsetInfo) throw new Error("MS SQL Server does not allow OFFSET without ORDER BY"); - else if (limitInfo) _processLimit(limitInfo); - } - - /** - * We need to turn LIMIT into a TOP clause on the SELECT STATEMENT - * - * @param limitInfo - * @private - */ - function _processLimit(limitInfo){ - var selectInfo=Mssql.super_.prototype.findNode.call(this, actions, "SELECT"); // jshint ignore:line - assert(selectInfo!==undefined,"MS SQL Server requires a SELECT clause when using LIMIT"); - // save the LIMIT node with the SELECT node - selectInfo.node.msSQLLimitNode=limitInfo.node; - // remove the LIMIT node from the filters so it doesn't get processed later. - filters.splice(limitInfo.index,1); - } - - /** - * We need to turn LIMIT into a TOP clause on the SELECT STATEMENT - * - * @param orderByInfo - * @param offsetInfo - * @param limitInfo - * @private - */ - function _processOrderByOffsetLimit(orderByInfo,offsetInfo,limitInfo){ - // save the OFFSET AND LIMIT nodes with the ORDER BY node - orderByInfo.node.msSQLOffsetNode=offsetInfo.node; - if (limitInfo) orderByInfo.node.msSQLLimitNode=limitInfo.node; - // remove the OFFSET and LIMIT nodes from the filters so they don't get processed later. - filters.splice(offsetInfo.index,1); - if (limitInfo) filters.splice(limitInfo.index,1); - } - - // MAIN + function _handleLimitAndOffset(){ + var limitInfo=Mssql.super_.prototype.findNode.call(this, filters, "LIMIT"); // jshint ignore:line + var offsetInfo=Mssql.super_.prototype.findNode.call(this, filters, "OFFSET"); // jshint ignore:line + var orderByInfo=Mssql.super_.prototype.findNode.call(this, filters, "ORDER BY"); // jshint ignore:line + + // no OFFSET or LIMIT then there's nothing special to do + if (!offsetInfo && !limitInfo) return; + // ORDER BY with OFFSET we have work to do, may consume LIMIT as well + if (orderByInfo && offsetInfo) _processOrderByOffsetLimit(orderByInfo,offsetInfo,limitInfo); + else if (offsetInfo) throw new Error("MS SQL Server does not allow OFFSET without ORDER BY"); + else if (limitInfo) _processLimit(limitInfo); + } + + /** + * We need to turn LIMIT into a TOP clause on the SELECT STATEMENT + * + * @param limitInfo + * @private + */ + function _processLimit(limitInfo){ + var selectInfo=Mssql.super_.prototype.findNode.call(this, actions, "SELECT"); // jshint ignore:line + assert(selectInfo!==undefined,"MS SQL Server requires a SELECT clause when using LIMIT"); + // save the LIMIT node with the SELECT node + selectInfo.node.msSQLLimitNode=limitInfo.node; + // remove the LIMIT node from the filters so it doesn't get processed later. + filters.splice(limitInfo.index,1); + } + + /** + * We need to turn LIMIT into a TOP clause on the SELECT STATEMENT + * + * @param orderByInfo + * @param offsetInfo + * @param limitInfo + * @private + */ + function _processOrderByOffsetLimit(orderByInfo,offsetInfo,limitInfo){ + // save the OFFSET AND LIMIT nodes with the ORDER BY node + orderByInfo.node.msSQLOffsetNode=offsetInfo.node; + if (limitInfo) orderByInfo.node.msSQLLimitNode=limitInfo.node; + // remove the OFFSET and LIMIT nodes from the filters so they don't get processed later. + filters.splice(offsetInfo.index,1); + if (limitInfo) filters.splice(limitInfo.index,1); + } + + // MAIN Mssql.super_.prototype.handleDistinct.call(this, actions, filters); - _handleLimitAndOffset(); - - // lazy-man sorting - var sortedNodes = actions.concat(targets).concat(filters); - for(var i = 0; i < sortedNodes.length; i++) { - var res = this.visit(sortedNodes[i]); - this.output = this.output.concat(res); - } - return this.output; + _handleLimitAndOffset(); + + // lazy-man sorting + var sortedNodes = actions.concat(targets).concat(filters); + for(var i = 0; i < sortedNodes.length; i++) { + var res = this.visit(sortedNodes[i]); + this.output = this.output.concat(res); + } + return this.output; }; //Mysql.prototype.visitRenameColumn = function(renameColumn) { @@ -373,19 +373,19 @@ Mssql.prototype.visitReturning = function() { // We deal with SELECT specially so we can add the TOP clause if needed Mssql.prototype.visitSelect = function(select) { - if (!select.msSQLLimitNode) return Mssql.super_.prototype.visitSelect.call(this, select); - var result=[ - 'SELECT', - 'TOP('+getModifierValue(this,select.msSQLLimitNode)+')', - select.nodes.map(this.visit.bind(this)).join(', ') - ]; - this._selectOrDeleteEndIndex = this.output.length + result.length; - return result; + if (!select.msSQLLimitNode) return Mssql.super_.prototype.visitSelect.call(this, select); + var result=[ + 'SELECT', + 'TOP('+getModifierValue(this,select.msSQLLimitNode)+')', + select.nodes.map(this.visit.bind(this)).join(', ') + ]; + this._selectOrDeleteEndIndex = this.output.length + result.length; + return result; }; // Node is either an OFFSET or LIMIT node function getModifierValue(dialect,node){ - return node.count.type ? dialect.visit(node.count) : node.count; + return node.count.type ? dialect.visit(node.count) : node.count; } function isAlterAddColumn(alter){ diff --git a/lib/dialect/oracle.js b/lib/dialect/oracle.js index 54343717..6b597154 100644 --- a/lib/dialect/oracle.js +++ b/lib/dialect/oracle.js @@ -144,7 +144,7 @@ Oracle.prototype.visitQueryHelper=function(actions,targets,filters){ output[limit+2] = temp[2]; } - return this.output; + return this.output; }; Oracle.prototype.visitColumn = function(columnNode) { diff --git a/lib/dialect/postgres.js b/lib/dialect/postgres.js index 764289b5..2e6c35de 100644 --- a/lib/dialect/postgres.js +++ b/lib/dialect/postgres.js @@ -649,15 +649,15 @@ Postgres.prototype.visitQuery = function(queryNode) { * @returns {String[]} */ Postgres.prototype.visitQueryHelper=function(actions,targets,filters){ - this.handleDistinct(actions, filters); - // lazy-man sorting - var sortedNodes = actions.concat(targets).concat(filters); - for(var i = 0; i < sortedNodes.length; i++) { - var res = this.visit(sortedNodes[i]); - this.output = this.output.concat(res); - } - // implicit 'from' - 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) { @@ -1002,11 +1002,11 @@ Postgres.prototype.visitCreateView = function(createView) { * @returns {Object|undefined} {index:number, node:Node} */ Postgres.prototype.findNode=function(list,type) { - for (var i= 0, len=list.length; i Date: Tue, 5 Jan 2016 10:21:55 -0800 Subject: [PATCH 084/153] jshint: added npm posttest lint --- package.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index c99ef1ea..08d5a618 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,9 @@ }, "main": "lib/", "scripts": { - "test": "node ./runtests" + "test": "node ./runtests", + "lint": "jshint lib test runtests.js", + "posttest": "jshint lib test runtests.js" }, "engines": { "node": "*" From 3553fd23f185d2838ea08c91a6d41afdeb35dfc3 Mon Sep 17 00:00:00 2001 From: Barry Hammen Date: Mon, 11 Jan 2016 12:25:21 -0500 Subject: [PATCH 085/153] Fix booleans and objects --- lib/dialect/postgres.js | 3 ++- lib/dialect/sqlite.js | 5 ++++ test/dialects/case-tests.js | 10 +++---- test/dialects/update-tests.js | 51 +++++++++++++++++++++++++++++++++++ 4 files changed, 63 insertions(+), 6 deletions(-) diff --git a/lib/dialect/postgres.js b/lib/dialect/postgres.js index c4b9eae8..c452f414 100644 --- a/lib/dialect/postgres.js +++ b/lib/dialect/postgres.js @@ -51,7 +51,8 @@ Postgres.prototype._getParameterValue = function(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'); diff --git a/lib/dialect/sqlite.js b/lib/dialect/sqlite.js index 611112e8..94a975e4 100644 --- a/lib/dialect/sqlite.js +++ b/lib/dialect/sqlite.js @@ -1,5 +1,6 @@ 'use strict'; +var _ = require('lodash'); var util = require('util'); var assert = require('assert'); @@ -23,6 +24,10 @@ Sqlite.prototype._getParameterValue = function(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); } diff --git a/test/dialects/case-tests.js b/test/dialects/case-tests.js index 046ee4b2..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`', @@ -40,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`', @@ -68,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`', @@ -96,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`', @@ -124,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`', diff --git a/test/dialects/update-tests.js b/test/dialects/update-tests.js index 5639c3a3..decfb4a5 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({ @@ -193,3 +194,53 @@ Harness.test({ }, 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 From dcd3f4bad1e9b27cc425902273a9ca7e3965e2ba Mon Sep 17 00:00:00 2001 From: Eric Perry Date: Tue, 12 Jan 2016 12:06:26 -0500 Subject: [PATCH 086/153] Fixed Postgres array syntax --- lib/dialect/postgres.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/dialect/postgres.js b/lib/dialect/postgres.js index c4b9eae8..182d04ec 100644 --- a/lib/dialect/postgres.js +++ b/lib/dialect/postgres.js @@ -42,7 +42,7 @@ Postgres.prototype._getParameterValue = function(value) { if (_.isArray(value)) { // convert each element of the array value = _.map(value, this._getParameterValue, this); - value = '(' + value.join(', ') + ')'; + value = '{' + value.join(', ') + '}'; } else if (_.isFunction(value.toISOString)) { // Date object's default toString format does not get parsed well // Handle date like objects using toISOString From 83428c872fcb8bb2e2a1b5cb5ffff5ad135aaeb5 Mon Sep 17 00:00:00 2001 From: Eric Perry Date: Tue, 12 Jan 2016 12:31:07 -0500 Subject: [PATCH 087/153] Add quotes around array --- lib/dialect/postgres.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/dialect/postgres.js b/lib/dialect/postgres.js index 182d04ec..7464ebfb 100644 --- a/lib/dialect/postgres.js +++ b/lib/dialect/postgres.js @@ -42,7 +42,7 @@ Postgres.prototype._getParameterValue = function(value) { if (_.isArray(value)) { // convert each element of the array value = _.map(value, this._getParameterValue, this); - value = '{' + value.join(', ') + '}'; + value = '\'{' + value.join(',') + '}\''; } else if (_.isFunction(value.toISOString)) { // Date object's default toString format does not get parsed well // Handle date like objects using toISOString From f6885792b09fcca737b7bbc8ade4c09397443f17 Mon Sep 17 00:00:00 2001 From: Eric Perry Date: Tue, 12 Jan 2016 12:39:40 -0500 Subject: [PATCH 088/153] Correctly double quote strings within arrays for PG --- lib/dialect/postgres.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/dialect/postgres.js b/lib/dialect/postgres.js index 7464ebfb..764c98e6 100644 --- a/lib/dialect/postgres.js +++ b/lib/dialect/postgres.js @@ -26,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'; @@ -36,12 +36,15 @@ 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.map(function (item) { + // In a Postgres array, strings must be double-quoted + return this._getParameterValue(item, '"'); + }); value = '\'{' + value.join(',') + '}\''; } else if (_.isFunction(value.toISOString)) { // Date object's default toString format does not get parsed well From bc10263b423d5e0680b349ce62bf3f3cd2cd351b Mon Sep 17 00:00:00 2001 From: Eric Perry Date: Tue, 12 Jan 2016 12:41:42 -0500 Subject: [PATCH 089/153] Alias 'this' because we're in a closure --- lib/dialect/postgres.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/dialect/postgres.js b/lib/dialect/postgres.js index 764c98e6..a8dbc449 100644 --- a/lib/dialect/postgres.js +++ b/lib/dialect/postgres.js @@ -41,9 +41,10 @@ Postgres.prototype._getParameterValue = function(value, quoteChar) { } else if ('object' === typeof value) { if (_.isArray(value)) { // convert each element of the array + var self = this; value = value.map(function (item) { // In a Postgres array, strings must be double-quoted - return this._getParameterValue(item, '"'); + return self._getParameterValue(item, '"'); }); value = '\'{' + value.join(',') + '}\''; } else if (_.isFunction(value.toISOString)) { From 74079157f6ba6c5786cc912e67d6d5a0c34018e6 Mon Sep 17 00:00:00 2001 From: Eric Perry Date: Wed, 13 Jan 2016 11:10:19 -0500 Subject: [PATCH 090/153] Added tests for postgres array insert --- lib/dialect/postgres.js | 15 ++++++++---- test/dialects/insert-tests.js | 46 +++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 5 deletions(-) diff --git a/lib/dialect/postgres.js b/lib/dialect/postgres.js index a8dbc449..28628ea6 100644 --- a/lib/dialect/postgres.js +++ b/lib/dialect/postgres.js @@ -42,11 +42,16 @@ Postgres.prototype._getParameterValue = function(value, quoteChar) { if (_.isArray(value)) { // convert each element of the array var self = this; - value = value.map(function (item) { - // In a Postgres array, strings must be double-quoted - return self._getParameterValue(item, '"'); - }); - value = '\'{' + value.join(',') + '}\''; + if (this._myClass === Postgres) { + 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, this); + value = '(' + value.join(', ') + ')'; + } } else if (_.isFunction(value.toISOString)) { // Date object's default toString format does not get parsed well // Handle date like objects using toISOString diff --git a/test/dialects/insert-tests.js b/test/dialects/insert-tests.js index b472f51d..b847020e 100644 --- a/test/dialects/insert-tests.js +++ b/test/dialects/insert-tests.js @@ -1,9 +1,15 @@ 'use strict'; +var Table = require(__dirname + '/../../lib/table'); var Harness = require('./support'); var post = Harness.definePostTable(); var user = Harness.defineUserTable(); +var arrayTable = Table.define({ + name: 'arraytest', + columns: ['id', 'numbers'] +}); + Harness.test({ query: post.insert(post.content.value('test'), post.userId.value(1)), pg: { @@ -630,3 +636,43 @@ 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\'))' + } +}); \ No newline at end of file From a84041c1250db2a269e032ae23298a9436c1fd3c Mon Sep 17 00:00:00 2001 From: Eric Perry Date: Wed, 13 Jan 2016 11:14:13 -0500 Subject: [PATCH 091/153] Moved variable declaration --- lib/dialect/postgres.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/dialect/postgres.js b/lib/dialect/postgres.js index 28628ea6..09297c51 100644 --- a/lib/dialect/postgres.js +++ b/lib/dialect/postgres.js @@ -41,8 +41,8 @@ Postgres.prototype._getParameterValue = function(value, quoteChar) { } else if ('object' === typeof value) { if (_.isArray(value)) { // convert each element of the array - var self = this; if (this._myClass === Postgres) { + var self = this; value = value.map(function (item) { // In a Postgres array, strings must be double-quoted return self._getParameterValue(item, '"'); From 6926e2166b212e044a1774dc2cf5e81e518650b4 Mon Sep 17 00:00:00 2001 From: brianc Date: Wed, 27 Jan 2016 11:30:43 -0600 Subject: [PATCH 092/153] Call mocha directly --- package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 08d5a618..2f63aac7 100644 --- a/package.json +++ b/package.json @@ -11,9 +11,9 @@ }, "main": "lib/", "scripts": { - "test": "node ./runtests", - "lint": "jshint lib test runtests.js", - "posttest": "jshint lib test runtests.js" + "test": "node_modules/.bin/mocha", + "lint": "jshint lib test", + "posttest": "jshint lib test" }, "engines": { "node": "*" From 51ccfa32606965d76159be01e81ec47409ba1d64 Mon Sep 17 00:00:00 2001 From: brianc Date: Wed, 27 Jan 2016 11:31:39 -0600 Subject: [PATCH 093/153] Test on more node versions --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 05d299e6..f04dabe6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,6 @@ language: node_js node_js: + - "5.0" + - "4.2" - "0.10" - "0.11" From 7f072ae52e9dba40a10fc5cdff487e8183358739 Mon Sep 17 00:00:00 2001 From: brianc Date: Wed, 27 Jan 2016 11:33:14 -0600 Subject: [PATCH 094/153] Revert "Merge pull request #290 from ericjperry/master" This reverts commit 7a640319d304f4f70ef831ca60050226a93f73c6, reversing changes made to cbd17ea230fb21c56aa7c19115d3ccbd7f9431a5. --- lib/dialect/postgres.js | 19 ++++----------- test/dialects/insert-tests.js | 46 ----------------------------------- 2 files changed, 5 insertions(+), 60 deletions(-) diff --git a/lib/dialect/postgres.js b/lib/dialect/postgres.js index d6c8a412..6dd4f8dc 100644 --- a/lib/dialect/postgres.js +++ b/lib/dialect/postgres.js @@ -26,7 +26,7 @@ Postgres.prototype._getParameterText = function(index, value) { } }; -Postgres.prototype._getParameterValue = function(value, quoteChar) { +Postgres.prototype._getParameterValue = function(value) { // handle primitives if (null === value) { value = 'NULL'; @@ -36,22 +36,13 @@ Postgres.prototype._getParameterValue = function(value, quoteChar) { // number is just number value = value; } else if ('string' === typeof value) { - // string uses single quote by default - value = this.quote(value, quoteChar || "'"); + // string uses single quote + value = this.quote(value, "'"); } else if ('object' === typeof value) { if (_.isArray(value)) { // convert each element of the array - if (this._myClass === Postgres) { - 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, this); - value = '(' + value.join(', ') + ')'; - } + value = _.map(value, this._getParameterValue, this); + value = '(' + value.join(', ') + ')'; } else if (_.isFunction(value.toISOString)) { // Date object's default toString format does not get parsed well // Handle date like objects using toISOString diff --git a/test/dialects/insert-tests.js b/test/dialects/insert-tests.js index b847020e..b472f51d 100644 --- a/test/dialects/insert-tests.js +++ b/test/dialects/insert-tests.js @@ -1,15 +1,9 @@ 'use strict'; -var Table = require(__dirname + '/../../lib/table'); var Harness = require('./support'); var post = Harness.definePostTable(); var user = Harness.defineUserTable(); -var arrayTable = Table.define({ - name: 'arraytest', - columns: ['id', 'numbers'] -}); - Harness.test({ query: post.insert(post.content.value('test'), post.userId.value(1)), pg: { @@ -636,43 +630,3 @@ 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\'))' - } -}); \ No newline at end of file From 8af5b48679e86d4a8e055208705682ed1d498dcd Mon Sep 17 00:00:00 2001 From: brianc Date: Wed, 27 Jan 2016 11:42:31 -0600 Subject: [PATCH 095/153] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2f63aac7..c6e2766f 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "author": "brianc ", "name": "sql", "description": "sql builder", - "version": "0.65.0", + "version": "0.66.0", "homepage": "https://github.com/brianc/node-sql", "license": "MIT", "repository": { From e0bd0630f1fcaf628dec5f8e5c937312750e1e1a Mon Sep 17 00:00:00 2001 From: Eric Perry Date: Thu, 28 Jan 2016 17:22:59 -0500 Subject: [PATCH 096/153] Updated array tests that Barry changed and fixed insertion of arrays of objects in Postgres --- lib/dialect/postgres.js | 22 ++++++++++++++--- test/dialects/insert-tests.js | 45 +++++++++++++++++++++++++++++++++++ test/dialects/update-tests.js | 2 +- 3 files changed, 65 insertions(+), 4 deletions(-) diff --git a/lib/dialect/postgres.js b/lib/dialect/postgres.js index 6dd4f8dc..0efad5bc 100644 --- a/lib/dialect/postgres.js +++ b/lib/dialect/postgres.js @@ -40,9 +40,25 @@ Postgres.prototype._getParameterValue = function(value) { value = this.quote(value, "'"); } else if ('object' === typeof value) { if (_.isArray(value)) { - // convert each element of the array - value = _.map(value, this._getParameterValue, this); - value = '(' + value.join(', ') + ')'; + 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) && + !_.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, this); + value = '(' + value.join(', ') + ')'; + } } else if (_.isFunction(value.toISOString)) { // Date object's default toString format does not get parsed well // Handle date like objects using toISOString diff --git a/test/dialects/insert-tests.js b/test/dialects/insert-tests.js index b472f51d..f9a08912 100644 --- a/test/dialects/insert-tests.js +++ b/test/dialects/insert-tests.js @@ -4,6 +4,11 @@ var Harness = require('./support'); var post = Harness.definePostTable(); var user = Harness.defineUserTable(); +var arrayTable = Table.define({ + name: 'arraytest', + columns: ['id', 'numbers'] +}); + Harness.test({ query: post.insert(post.content.value('test'), post.userId.value(1)), pg: { @@ -630,3 +635,43 @@ 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\'))' + } +}); diff --git a/test/dialects/update-tests.js b/test/dialects/update-tests.js index decfb4a5..eb3d0b3e 100644 --- a/test/dialects/update-tests.js +++ b/test/dialects/update-tests.js @@ -228,7 +228,7 @@ Harness.test({ }), 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}\')' + 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', From 91b34eceafc2774c46f2a1a052472d179dc7d70e Mon Sep 17 00:00:00 2001 From: Eric Perry Date: Thu, 28 Jan 2016 17:29:00 -0500 Subject: [PATCH 097/153] Fixed some rebase issues --- lib/dialect/postgres.js | 6 +++--- test/dialects/insert-tests.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/dialect/postgres.js b/lib/dialect/postgres.js index 0efad5bc..d92fa060 100644 --- a/lib/dialect/postgres.js +++ b/lib/dialect/postgres.js @@ -26,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'; @@ -36,8 +36,8 @@ 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)) { if (this._myClass === Postgres) { diff --git a/test/dialects/insert-tests.js b/test/dialects/insert-tests.js index f9a08912..804daa06 100644 --- a/test/dialects/insert-tests.js +++ b/test/dialects/insert-tests.js @@ -4,7 +4,7 @@ var Harness = require('./support'); var post = Harness.definePostTable(); var user = Harness.defineUserTable(); -var arrayTable = Table.define({ +var arrayTable = require('../../lib/table').define({ name: 'arraytest', columns: ['id', 'numbers'] }); From d0cc3c7449230dd4b3058b9eba172f8a681365a3 Mon Sep 17 00:00:00 2001 From: brianc Date: Fri, 29 Jan 2016 10:57:44 -0600 Subject: [PATCH 098/153] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c6e2766f..b2d7af1c 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "author": "brianc ", "name": "sql", "description": "sql builder", - "version": "0.66.0", + "version": "0.67.0", "homepage": "https://github.com/brianc/node-sql", "license": "MIT", "repository": { From e734df273cfb174d4d88fc2078818297b27855b3 Mon Sep 17 00:00:00 2001 From: tmont Date: Mon, 1 Feb 2016 11:25:54 -0800 Subject: [PATCH 099/153] upgraded to lodash@4.1.x fixed #296 --- lib/dialect/postgres.js | 2 +- lib/table.js | 2 +- package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/dialect/postgres.js b/lib/dialect/postgres.js index d92fa060..d29894d9 100644 --- a/lib/dialect/postgres.js +++ b/lib/dialect/postgres.js @@ -56,7 +56,7 @@ Postgres.prototype._getParameterValue = function(value, quoteChar) { value = '\'{' + value.join(',') + '}\''; } } else { - value = _.map(value, this._getParameterValue, this); + value = _.map(value, this._getParameterValue.bind(this)); value = '(' + value.join(', ') + ')'; } } else if (_.isFunction(value.toISOString)) { diff --git a/lib/table.js b/lib/table.js index 86e8adbe..debafa24 100644 --- a/lib/table.js +++ b/lib/table.js @@ -78,7 +78,7 @@ Table.prototype.createColumn = function(col) { name: subfield })]; }, this)) - .object() + .fromPairs() .value(); } } diff --git a/package.json b/package.json index b2d7af1c..941b3329 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ }, "dependencies": { "sliced": "0.0.x", - "lodash": "1.3.x" + "lodash": "4.1.x" }, "devDependencies": { "jshint": "*", From 02b6f064c586efba3a4a0435670ccc4bd7ab77f9 Mon Sep 17 00:00:00 2001 From: tmont Date: Mon, 1 Feb 2016 11:31:01 -0800 Subject: [PATCH 100/153] removed dead code --- lib/dialect/postgres.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/dialect/postgres.js b/lib/dialect/postgres.js index d92fa060..520dc7ab 100644 --- a/lib/dialect/postgres.js +++ b/lib/dialect/postgres.js @@ -1006,7 +1006,6 @@ Postgres.prototype.visitDropIndex = function(node) { }; Postgres.prototype.visitCreateView = function(createView) { - //console.log('createView: ' + createView); var result = ['CREATE VIEW', this.quote(createView.options.viewName), 'AS']; return result; }; From 2e00b11f5c4aab77938ada67472efefab3e25521 Mon Sep 17 00:00:00 2001 From: tmont Date: Mon, 1 Feb 2016 11:31:17 -0800 Subject: [PATCH 101/153] only log stuff to the console if NODE_ENV=debug fixes #275 --- lib/table.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/table.js b/lib/table.js index 86e8adbe..725d6666 100644 --- a/lib/table.js +++ b/lib/table.js @@ -98,7 +98,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); From 775e93829a5a0d57c5e0d22302fd04cac9d8764b Mon Sep 17 00:00:00 2001 From: Dan Rzeppa Date: Mon, 8 Feb 2016 13:10:32 -0500 Subject: [PATCH 102/153] Extend query with value expressions to create allow creating select statements that can be joined together with expressions. --- lib/dialect/postgres.js | 3 +- lib/node/index.js | 6 +- lib/node/query.js | 14 +++++ test/dialects/select-tests.js | 104 +++++++++++++++++++++++++++++++++- 4 files changed, 124 insertions(+), 3 deletions(-) diff --git a/lib/dialect/postgres.js b/lib/dialect/postgres.js index d29894d9..254511a3 100644 --- a/lib/dialect/postgres.js +++ b/lib/dialect/postgres.js @@ -644,7 +644,8 @@ Postgres.prototype.visitQuery = function(queryNode) { actions.push(new Select().add('*')); isSelect = true; } - if(missingFrom) { + if(missingFrom && queryNode.table instanceof Table) { + // the instanceof handles the situation where a sql.select(some expression) is used and there should be no FROM clause targets.push(new From().add(queryNode.table)); } if (createView) { diff --git a/lib/node/index.js b/lib/node/index.js index b500ab39..0bff1586 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; }; diff --git a/lib/node/query.js b/lib/node/query.js index 4f13ebd1..46ec6cad 100644 --- a/lib/node/query.js +++ b/lib/node/query.js @@ -1,8 +1,10 @@ 'use strict'; +var _ = require('lodash'); var assert = require('assert'); var sliced = require('sliced'); var util = require('util'); +var valueExpressionMixin = require(__dirname + '/valueExpression'); var Node = require('./'); var Select = require('./select'); @@ -478,4 +480,16 @@ var Query = Node.define({ } }); +// 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); + module.exports = Query; diff --git a/test/dialects/select-tests.js b/test/dialects/select-tests.js index 59fdd786..0825f95a 100644 --- a/test/dialects/select-tests.js +++ b/test/dialects/select-tests.js @@ -3,6 +3,7 @@ var Harness = require('./support'); var post = Harness.definePostTable(); var customerAlias = Harness.defineCustomerAliasTable(); +var Sql = require('../../lib'); Harness.test({ query: post.select(post.id).select(post.content), @@ -52,4 +53,105 @@ Harness.test({ 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: [] -}); \ No newline at end of file +}); + +// 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: [] +}); + +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: [] +}); From 69b3173577f09dbd6e534115952c758851349072 Mon Sep 17 00:00:00 2001 From: Dan Rzeppa Date: Mon, 8 Feb 2016 13:14:37 -0500 Subject: [PATCH 103/153] Added another test for generating select clauses without from clauses. --- test/dialects/select-tests.js | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/test/dialects/select-tests.js b/test/dialects/select-tests.js index 0825f95a..baa3eb40 100644 --- a/test/dialects/select-tests.js +++ b/test/dialects/select-tests.js @@ -81,6 +81,32 @@ Harness.test({ 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: { From 50e184c62af3e16eb20168fe7f359ece44cca353 Mon Sep 17 00:00:00 2001 From: Dan Rzeppa Date: Mon, 8 Feb 2016 18:23:29 -0500 Subject: [PATCH 104/153] Fixed a couple of jshint errors. --- lib/node/index.js | 8 ++++---- lib/node/query.js | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/node/index.js b/lib/node/index.js index 0bff1586..d519466f 100644 --- a/lib/node/index.js +++ b/lib/node/index.js @@ -15,10 +15,10 @@ Node.prototype.toNode = function() { Node.prototype.add = function(node) { assert(node, 'Error while trying to add a non-existant node to a query'); - var newNode - if (typeof node === 'string') newNode = new TextNode(node) - else if (node.toNode) newNode = node.toNode() - else newNode = node + 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; }; diff --git a/lib/node/query.js b/lib/node/query.js index 46ec6cad..cec925e5 100644 --- a/lib/node/query.js +++ b/lib/node/query.js @@ -488,8 +488,8 @@ var Query = Node.define({ // 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"]; +delete valueExpressions.or; +delete valueExpressions.and; _.extend(Query.prototype, valueExpressions); module.exports = Query; From 687eecf3504062c4a697b49e1931ebd4bff6bd4a Mon Sep 17 00:00:00 2001 From: Dan Rzeppa Date: Sat, 20 Feb 2016 10:36:33 -0500 Subject: [PATCH 105/153] Addresses issue when generating INSERT INTO ... SELECT statements created using the .add() function. --- lib/dialect/postgres.js | 25 ++++++++- test/dialects/insert-tests.js | 101 ++++++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+), 2 deletions(-) diff --git a/lib/dialect/postgres.js b/lib/dialect/postgres.js index cbc7ece6..1b664504 100644 --- a/lib/dialect/postgres.js +++ b/lib/dialect/postgres.js @@ -596,7 +596,7 @@ Postgres.prototype.visitOverlap = function(overlap) { }; Postgres.prototype.visitQuery = function(queryNode) { - if (this._queryNode) return this.visitSubquery(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 @@ -678,7 +678,7 @@ Postgres.prototype.visitQueryHelper=function(actions,targets,filters){ 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(this.config); @@ -696,6 +696,9 @@ Postgres.prototype.visitSubquery = function(queryNode) { } var alias = queryNode.alias; + if (dontParenthesize) { + return [subQuery.output.join(' ') + (alias ? ' ' + this.quote(alias) : '')]; + } return ['(' + subQuery.output.join(' ') + ')' + (alias ? ' ' + this.quote(alias) : '')]; }; @@ -1041,4 +1044,22 @@ Postgres.prototype.handleDistinct = function(actions,filters) { selectInfo.node.isDistinct = true; }; +/** + * If the parent of the subquery is an INSERT we don't want to parenthesize. + * This happens when you create the query like so: + * + * var query=post.insert(post.id) + * var select=user.select(user.id) + * query.add(select) + * + * @param parentQuery + * @returns {boolean} + */ +function dontParenthesizeSubQuery(parentQuery){ + if (!parentQuery) return false; + if (parentQuery.nodes.length == 0) return false; + if (parentQuery.nodes[0].type != 'INSERT') return false; + return true; +} + module.exports = Postgres; diff --git a/test/dialects/insert-tests.js b/test/dialects/insert-tests.js index 804daa06..73517b48 100644 --- a/test/dialects/insert-tests.js +++ b/test/dialects/insert-tests.js @@ -675,3 +675,104 @@ Harness.test({ 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).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: [] +}); + From 046d6dc19f712f1b7f3683ffabf15210d9ee7c5e Mon Sep 17 00:00:00 2001 From: iamcharliegoddard Date: Fri, 18 Mar 2016 22:00:46 -0700 Subject: [PATCH 106/153] jshint-fix: fix for jshint error --- lib/dialect/postgres.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/dialect/postgres.js b/lib/dialect/postgres.js index 6539a54f..50f78233 100644 --- a/lib/dialect/postgres.js +++ b/lib/dialect/postgres.js @@ -1061,7 +1061,7 @@ Postgres.prototype.handleDistinct = function(actions,filters) { */ function dontParenthesizeSubQuery(parentQuery){ if (!parentQuery) return false; - if (parentQuery.nodes.length == 0) return false; + if (parentQuery.nodes.length === 0) return false; if (parentQuery.nodes[0].type != 'INSERT') return false; return true; } From 43b6c350950b70f340cc22ca0ce2668b6626073b Mon Sep 17 00:00:00 2001 From: Kevin Anthoney Date: Fri, 1 Apr 2016 20:43:15 +0100 Subject: [PATCH 107/153] Add table level foreign keys * Add table level foreign keys * Make refColumns optional in foreign keys * Add constraint name to foreign keys * Add on update clause to foreign keys, and also to column references while I was at it * Add actions SET NULL, SET DEFAULT and NO ACTION for onDelete and onUpdate for foreign keys and column references --- lib/dialect/postgres.js | 67 +++++++++++++++++- lib/node/foreignKey.js | 19 ++++++ lib/table.js | 15 +++- test/dialects/create-table-tests.js | 102 +++++++++++++++++++++++++--- 4 files changed, 191 insertions(+), 12 deletions(-) create mode 100644 lib/node/foreignKey.js diff --git a/lib/dialect/postgres.js b/lib/dialect/postgres.js index 50f78233..6a7c0dc7 100644 --- a/lib/dialect/postgres.js +++ b/lib/dialect/postgres.js @@ -153,6 +153,7 @@ Postgres.prototype.visit = function(node) { 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; @@ -296,6 +297,7 @@ 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']; if (create.options.isTemporary) result=['CREATE TEMPORARY TABLE']; @@ -313,6 +315,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; @@ -836,9 +841,14 @@ Postgres.prototype.visitColumn = function(columnNode) { var onDelete = columnNode.references.onDelete; if (onDelete) onDelete = onDelete.toUpperCase(); - if (onDelete === 'CASCADE' || onDelete === 'RESTRICT') { + 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(); @@ -850,6 +860,61 @@ Postgres.prototype.visitColumn = function(columnNode) { return [txt.join('')]; }; +Postgres.prototype.visitForeignKey = function(foreignKeyNode) +{ + var txt = []; + if(this._visitingCreate) { + assert(foreignKeyNode.table, 'Foreign table missing for table reference'); + assert(foreignKeyNode.columns, 'Columns missing for table reference'); + if(foreignKeyNode.refColumns !== undefined) { + assert.equal(foreignKeyNode.columns.length, foreignKeyNode.refColumns.length, 'Number of local columns and foreign columns differ in table reference'); + } + if(foreignKeyNode.name !== undefined) { + txt.push('CONSTRAINT ' + this.quote(foreignKeyNode.name) + ' '); + } + txt.push('FOREIGN KEY ( '); + for(var i = 0; i < foreignKeyNode.columns.length; i++) { + if(i>0) { + txt.push(', '); + } + txt.push(this.quote(foreignKeyNode.columns[i])); + } + txt.push(' ) REFERENCES '); + if(foreignKeyNode.schema !== undefined) { + txt.push(this.quote(foreignKeyNode.schema) + '.'); + } + txt.push(this.quote(foreignKeyNode.table)); + if(foreignKeyNode.refColumns !== undefined) { + txt.push(' ( '); + for(i = 0; i < foreignKeyNode.refColumns.length; i++) { + if(i>0) { + txt.push(', '); + } + txt.push(this.quote(foreignKeyNode.refColumns[i])); + } + txt.push(' )'); + } + var onDelete = foreignKeyNode.onDelete; + if(onDelete) { + onDelete = onDelete.toUpperCase(); + if(onDelete === 'CASCADE' || onDelete === 'RESTRICT' || onDelete === 'SET NULL' || onDelete === 'SET DEFAULT' || onDelete === 'NO ACTION') { + txt.push(' ON DELETE ' + onDelete); + } + } + var onUpdate = foreignKeyNode.onUpdate; + if(onUpdate) { + onUpdate = onUpdate.toUpperCase(); + if(onUpdate === 'CASCADE' || onUpdate === 'RESTRICT' || onUpdate === 'SET NULL' || onUpdate === 'SET DEFAULT' || onUpdate === 'NO ACTION') { + txt.push(' ON UPDATE ' + onUpdate); + } + } + if(foreignKeyNode.constraint) { + txt.push(' ' + foreignKeyNode.constraint.toUpperCase()); + } + } + return [txt.join('')]; +}; + Postgres.prototype.visitFunctionCall = function(functionCall) { this._visitingFunctionCall = true; var txt = functionCall.name + '(' + functionCall.nodes.map(this.visit.bind(this)).join(', ') + ')'; diff --git a/lib/node/foreignKey.js b/lib/node/foreignKey.js new file mode 100644 index 00000000..c738d842 --- /dev/null +++ b/lib/node/foreignKey.js @@ -0,0 +1,19 @@ +'use strict'; + +var Node = require(__dirname); + +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/table.js b/lib/table.js index b810283c..21895aea 100644 --- a/lib/table.js +++ b/lib/table.js @@ -9,6 +9,7 @@ var TableNode = require(__dirname + '/node/table'); var JoinNode = require(__dirname + '/node/join'); var LiteralNode = require(__dirname + '/node/literal'); var Joiner = require(__dirname + '/joiner'); +var ForeignKeyNode = require(__dirname + '/node/foreignKey'); var Table = function(config) { this._schema = config.schema; @@ -18,6 +19,7 @@ var Table = function(config) { this.isTemporary=!!config.isTemporary; this.snakeToCamel = !!config.snakeToCamel; this.columns = []; + this.foreignKeys = []; this.table = this; if (!config.sql) { config.sql = require('./index'); @@ -45,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; }; @@ -55,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 || {})); }; diff --git a/test/dialects/create-table-tests.js b/test/dialects/create-table-tests.js index 872d51fa..97f6a1fd 100644 --- a/test/dialects/create-table-tests.js +++ b/test/dialects/create-table-tests.js @@ -256,25 +256,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"))', - 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)' }, params: [] }); @@ -643,4 +645,84 @@ Harness.test({ string: 'CREATE TABLE "post" ("id" int PRIMARY KEY)' }, params: [] -}); \ No newline at end of file +}); + +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: [] +}); From 297daf305f12e14e6053bdef9d1ad17af8df7a0c Mon Sep 17 00:00:00 2001 From: brianc Date: Wed, 18 May 2016 17:52:10 -0400 Subject: [PATCH 108/153] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 941b3329..0e0cb5cd 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "author": "brianc ", "name": "sql", "description": "sql builder", - "version": "0.67.0", + "version": "0.68.0", "homepage": "https://github.com/brianc/node-sql", "license": "MIT", "repository": { From e00acabe5662fa80dc12da6b2581279e992e8e7b Mon Sep 17 00:00:00 2001 From: Barry Hammen Date: Mon, 23 May 2016 15:29:34 -0400 Subject: [PATCH 109/153] Add support for basic date functions (#318) --- lib/dialect/mssql.js | 24 +++++-- lib/dialect/mysql.js | 25 +++++++ lib/dialect/postgres.js | 16 ++++- lib/dialect/sqlite.js | 41 +++++++++--- lib/functions.js | 10 ++- test/dialects/date-tests.js | 130 ++++++++++++++++++++++++++++++++++++ 6 files changed, 232 insertions(+), 14 deletions(-) create mode 100644 test/dialects/date-tests.js diff --git a/lib/dialect/mssql.js b/lib/dialect/mssql.js index 48c1ad45..d8e299e8 100644 --- a/lib/dialect/mssql.js +++ b/lib/dialect/mssql.js @@ -246,10 +246,26 @@ Mssql.prototype.visitDrop = function(drop) { Mssql.prototype.visitFunctionCall = function(functionCall) { this._visitingFunctionCall = true; - var name=functionCall.name; - // override the LENGTH function since mssql calls it LEN - if (name=="LENGTH") name="LEN"; - var txt = name + '(' + functionCall.nodes.map(this.visit.bind(this)).join(', ') + ')'; + 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]; }; diff --git a/lib/dialect/mysql.js b/lib/dialect/mysql.js index 9d582ce7..225ea6be 100644 --- a/lib/dialect/mysql.js +++ b/lib/dialect/mysql.js @@ -102,4 +102,29 @@ Mysql.prototype.visitBinary = function(binary) { 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]; +}; + + module.exports = Mysql; diff --git a/lib/dialect/postgres.js b/lib/dialect/postgres.js index 6a7c0dc7..ab12869d 100644 --- a/lib/dialect/postgres.js +++ b/lib/dialect/postgres.js @@ -917,7 +917,21 @@ Postgres.prototype.visitForeignKey = function(foreignKeyNode) Postgres.prototype.visitFunctionCall = function(functionCall) { this._visitingFunctionCall = true; - var txt = functionCall.name + '(' + functionCall.nodes.map(this.visit.bind(this)).join(', ') + ')'; + 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]; }; diff --git a/lib/dialect/sqlite.js b/lib/dialect/sqlite.js index 81b452c0..d2885c2f 100644 --- a/lib/dialect/sqlite.js +++ b/lib/dialect/sqlite.js @@ -42,16 +42,16 @@ Sqlite.prototype.visitDropColumn = function() { throw new Error('SQLite does not allow dropping columns.'); }; -Sqlite.prototype.visitFunctionCall = function(functionCall) { - var _this=this; +Sqlite.prototype.visitFunctionCall = function (functionCall) { + var _this = this; - this._visitingFunctionCall = true; + 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]+'') + ')'; + var txt = "SUBSTR(" + (nodes[0] + '') + ', 1, ' + (nodes[1] + '') + ')'; return txt; } @@ -59,18 +59,43 @@ Sqlite.prototype.visitFunctionCall = function(functionCall) { // 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]+'') + ')'; + var txt = "SUBSTR(" + (nodes[0] + '') + ', -' + (nodes[1] + '') + ')'; return txt; } - var txt=""; - var name=functionCall.name; + 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 txt = 'strftime(' + format + ', ' + (nodes[0] + '') + ')'; + 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; + this._visitingFunctionCall = false; return [txt]; }; diff --git a/lib/functions.js b/lib/functions.js index ef9b6f81..b0abcf54 100644 --- a/lib/functions.js +++ b/lib/functions.js @@ -47,13 +47,21 @@ 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() { diff --git a/test/dialects/date-tests.js b/test/dialects/date-tests.js new file mode 100644 index 00000000..6eaf9851 --- /dev/null +++ b/test/dialects/date-tests.js @@ -0,0 +1,130 @@ +'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\', "customer"."metadata") FROM "customer"', + string: 'SELECT strftime(\'%m\', "customer"."metadata") FROM "customer"' + }, + 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\', "customer"."metadata") FROM "customer"', + string: 'SELECT strftime(\'%H\', "customer"."metadata") FROM "customer"' + }, + 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: [] +}); \ No newline at end of file From eec86e41ed0dfcbf51891dfed97df1d93995a3b3 Mon Sep 17 00:00:00 2001 From: brianc Date: Mon, 23 May 2016 14:30:07 -0500 Subject: [PATCH 110/153] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0e0cb5cd..06159cf6 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "author": "brianc ", "name": "sql", "description": "sql builder", - "version": "0.68.0", + "version": "0.69.0", "homepage": "https://github.com/brianc/node-sql", "license": "MIT", "repository": { From bc4d8d9dd00cf4e4ff8b69e5e2cca13f896e70fa Mon Sep 17 00:00:00 2001 From: Barry Hammen Date: Thu, 26 May 2016 13:14:28 -0400 Subject: [PATCH 111/153] Fix sqlite date functions for epoch (#319) --- lib/dialect/sqlite.js | 8 +++++++- test/dialects/date-tests.js | 14 ++++++++++---- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/lib/dialect/sqlite.js b/lib/dialect/sqlite.js index d2885c2f..6085ba21 100644 --- a/lib/dialect/sqlite.js +++ b/lib/dialect/sqlite.js @@ -81,7 +81,13 @@ Sqlite.prototype.visitFunctionCall = function (functionCall) { format = "'%H'"; break; } - var txt = 'strftime(' + format + ', ' + (nodes[0] + '') + ')'; + 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; } diff --git a/test/dialects/date-tests.js b/test/dialects/date-tests.js index 6eaf9851..5a5fadda 100644 --- a/test/dialects/date-tests.js +++ b/test/dialects/date-tests.js @@ -36,8 +36,11 @@ Harness.test({ string: 'SELECT EXTRACT(MONTH FROM "customer"."metadata") FROM "customer"' }, sqlite: { - text : 'SELECT strftime(\'%m\', "customer"."metadata") FROM "customer"', - string: 'SELECT strftime(\'%m\', "customer"."metadata") FROM "customer"' + 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`', @@ -86,8 +89,11 @@ Harness.test({ string: 'SELECT EXTRACT(HOUR FROM "customer"."metadata") FROM "customer"' }, sqlite: { - text : 'SELECT strftime(\'%H\', "customer"."metadata") FROM "customer"', - string: 'SELECT strftime(\'%H\', "customer"."metadata") FROM "customer"' + 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`', From e6b40a96683cfe4bdd079a096243c3ba877035cf Mon Sep 17 00:00:00 2001 From: brianc Date: Thu, 26 May 2016 12:14:41 -0500 Subject: [PATCH 112/153] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 06159cf6..3503e59d 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "author": "brianc ", "name": "sql", "description": "sql builder", - "version": "0.69.0", + "version": "0.70.0", "homepage": "https://github.com/brianc/node-sql", "license": "MIT", "repository": { From cf928eeb2bd4305ed576ba32d1bcc01c55630a17 Mon Sep 17 00:00:00 2001 From: Brad Dunbar Date: Thu, 16 Jun 2016 18:44:28 -0400 Subject: [PATCH 113/153] _.isArray -> Array.isArray (#309) --- lib/dialect/postgres.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/dialect/postgres.js b/lib/dialect/postgres.js index ab12869d..fb7fb4d4 100644 --- a/lib/dialect/postgres.js +++ b/lib/dialect/postgres.js @@ -39,13 +39,13 @@ Postgres.prototype._getParameterValue = function(value, quoteChar) { // string uses single quote by default value = this.quote(value, quoteChar || "'"); } else if ('object' === typeof value) { - if (_.isArray(value)) { + if (Array.isArray(value)) { if (this._myClass === Postgres) { // naive check to see if this is an array of objects, which // is handled differently than an array of primitives if (value.length && 'object' === typeof value[0] && !_.isFunction(value[0].toISOString) && - !_.isArray(value[0])) { + !Array.isArray(value[0])) { value = "'" + JSON.stringify(value) + "'"; } else { var self = this; From 6786f50b89489a311e1a5f3e045f0e0a6078d629 Mon Sep 17 00:00:00 2001 From: Kevin Anthoney Date: Wed, 20 Jul 2016 20:14:41 +0100 Subject: [PATCH 114/153] Use count(*) syntax for MySQL instead of count(`table`.*) (#325) --- lib/dialect/mysql.js | 24 ++++++++++++++++++++++++ test/dialects/aggregate-tests.js | 16 ++++++++-------- 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/lib/dialect/mysql.js b/lib/dialect/mysql.js index 225ea6be..86416fcf 100644 --- a/lib/dialect/mysql.js +++ b/lib/dialect/mysql.js @@ -126,5 +126,29 @@ Mysql.prototype.visitFunctionCall = function(functionCall) { 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); +}; module.exports = Mysql; diff --git a/test/dialects/aggregate-tests.js b/test/dialects/aggregate-tests.js index d67d775a..59d48d57 100644 --- a/test/dialects/aggregate-tests.js +++ b/test/dialects/aggregate-tests.js @@ -16,8 +16,8 @@ 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]', @@ -41,8 +41,8 @@ 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]', @@ -66,8 +66,8 @@ 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]', @@ -166,8 +166,8 @@ 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"', From b987e5c9dbd14cd8d4a34a9d245c1daf8a8b65e1 Mon Sep 17 00:00:00 2001 From: brianc Date: Wed, 20 Jul 2016 14:14:56 -0500 Subject: [PATCH 115/153] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3503e59d..5a82fb8d 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "author": "brianc ", "name": "sql", "description": "sql builder", - "version": "0.70.0", + "version": "0.70.1", "homepage": "https://github.com/brianc/node-sql", "license": "MIT", "repository": { From eba2de6dc7a6b272ee94a7ad56eb6434a5a50305 Mon Sep 17 00:00:00 2001 From: danrzeppa Date: Tue, 30 Aug 2016 09:30:25 -0400 Subject: [PATCH 116/153] Alias a query with AS (#329) * Added a sql.literalColumn function for selecting literal/constant values. * - Added tests for function call on a literal column * Converted tabs to spaces. * Renamed "literalColumn" to "constant" * Added missing semicolons causing the Travis build to fail. * Added missing semicolon * Added ability to attach AS to a query. --- lib/dialect/mssql.js | 2 +- lib/dialect/oracle.js | 2 +- lib/dialect/postgres.js | 28 ++-- lib/index.js | 31 +++- lib/node/column.js | 2 + lib/node/query.js | 7 + test/dialects/select-tests.js | 278 ++++++++++++++++++++++++++++++++++ 7 files changed, 331 insertions(+), 19 deletions(-) diff --git a/lib/dialect/mssql.js b/lib/dialect/mssql.js index d8e299e8..2cb95d9d 100644 --- a/lib/dialect/mssql.js +++ b/lib/dialect/mssql.js @@ -186,7 +186,7 @@ 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); }; diff --git a/lib/dialect/oracle.js b/lib/dialect/oracle.js index 6b597154..ea2cb85e 100644 --- a/lib/dialect/oracle.js +++ b/lib/dialect/oracle.js @@ -168,7 +168,7 @@ Oracle.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 Oracle.super_.prototype.visitColumn.call(this, columnNode); }; diff --git a/lib/dialect/postgres.js b/lib/dialect/postgres.js index fb7fb4d4..a57e6402 100644 --- a/lib/dialect/postgres.js +++ b/lib/dialect/postgres.js @@ -738,7 +738,7 @@ Postgres.prototype.visitColumn = function(columnNode) { 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+'('); @@ -755,16 +755,18 @@ Postgres.prototype.visitColumn = function(columnNode) { } } if(!inInsertUpdateClause && !this.visitingReturning && !this._visitingCreate && !this._visitingAlter && !columnNode.subfieldContainer) { - if(typeof table.alias === 'string') { - txt.push(this.quote(table.alias)); - } else { - if(table.getSchema()) { - txt.push(this.quote(table.getSchema())); - txt.push('.'); + 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 = []; @@ -785,6 +787,14 @@ 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) + ').'); diff --git a/lib/index.js b/lib/index.js index 6f609570..1309fcf7 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,13 +1,15 @@ 'use strict'; -var _ = require('lodash'); -var FunctionCall = require('./node/functionCall'); -var ArrayCall = require('./node/arrayCall'); -var functions = require('./functions'); -var getDialect = require('./dialect'); -var Query = require('./node/query'); -var sliced = require('sliced'); -var Table = require('./table'); +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 ParameterNode = require('./node/parameter'); +var Query = require('./node/query'); +var sliced = require('sliced'); +var Table = require('./table'); // default dialect is postgres var DEFAULT_DIALECT = 'postgres'; @@ -58,6 +60,19 @@ Sql.prototype.setDialect = function(dialect, config) { return this; }; +// Create a constant Column (for use in SELECT) +Sql.prototype.constant = function(value) { + var config={ + name:"constant", + property:"constant", + isConstant:true, + constantValue:value, + }; + var cn = new Column(config); + return cn; +}; + + // back compat shim for the Sql class constructor var create = function(dialect, config) { return new Sql(dialect, {}); diff --git a/lib/node/column.js b/lib/node/column.js index 6fa999fb..6a5f7284 100644 --- a/lib/node/column.js +++ b/lib/node/column.js @@ -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; diff --git a/lib/node/query.js b/lib/node/query.js index cec925e5..6c7ca8fd 100644 --- a/lib/node/query.js +++ b/lib/node/query.js @@ -1,6 +1,7 @@ 'use strict'; var _ = require('lodash'); +var alias = require(__dirname + '/alias'); var assert = require('assert'); var sliced = require('sliced'); var util = require('util'); @@ -492,4 +493,10 @@ 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/test/dialects/select-tests.js b/test/dialects/select-tests.js index baa3eb40..2892c8ae 100644 --- a/test/dialects/select-tests.js +++ b/test/dialects/select-tests.js @@ -2,6 +2,7 @@ var Harness = require('./support'); var post = Harness.definePostTable(); +var user = Harness.defineUserTable(); var customerAlias = Harness.defineCustomerAliasTable(); var Sql = require('../../lib'); @@ -181,3 +182,280 @@ Harness.test({ }, 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: [] +}); + + From 5ec7827cf637a4fe6b930fd4e8d27e6a8cb5289f Mon Sep 17 00:00:00 2001 From: "Brian M. Carlson" Date: Tue, 30 Aug 2016 03:32:46 -0500 Subject: [PATCH 117/153] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5a82fb8d..64e2f317 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "author": "brianc ", "name": "sql", "description": "sql builder", - "version": "0.70.1", + "version": "0.71.0", "homepage": "https://github.com/brianc/node-sql", "license": "MIT", "repository": { From f10abbed79a61e05c0a608095b76b9cf0e44b4fb Mon Sep 17 00:00:00 2001 From: Rohit Singh Date: Wed, 19 Oct 2016 16:54:02 +0200 Subject: [PATCH 118/153] Add support for ON CONFLICT as a support for upserts in PostgreSQL (#335) * Add support for ON CONFLICT as a support for upserts in PostgreSQL PostgreSQL supports upserts by ON CONFLICT clause provided in versions 9.5 and above. This should fix issue #333 * avoid redefining variable * fix scope --- lib/dialect/mssql.js | 4 + lib/dialect/mysql.js | 4 + lib/dialect/oracle.js | 4 + lib/dialect/postgres.js | 31 ++++++ lib/dialect/sqlite.js | 4 + lib/node/onConflict.js | 7 ++ lib/node/query.js | 10 ++ test/dialects/insert-tests.js | 184 ++++++++++++++++++++++++++++++++++ 8 files changed, 248 insertions(+) create mode 100644 lib/node/onConflict.js diff --git a/lib/dialect/mssql.js b/lib/dialect/mssql.js index 2cb95d9d..e93fc45a 100644 --- a/lib/dialect/mssql.js +++ b/lib/dialect/mssql.js @@ -381,6 +381,10 @@ Mssql.prototype.visitOnDuplicate = function(onDuplicate) { throw new Error('MSSQL does not allow onDuplicate clause.'); }; +Mssql.prototype.visitOnConflict = function(onConflict) { + throw new Error('MSSQL does not allow onConflict clause.'); +}; + Mssql.prototype.visitReturning = function() { // TODO: need to add some code to the INSERT clause to support this since its the equivalent of the OUTPUT clause // in MS SQL which appears before the values, not at the end of the statement. diff --git a/lib/dialect/mysql.js b/lib/dialect/mysql.js index 86416fcf..4d054564 100644 --- a/lib/dialect/mysql.js +++ b/lib/dialect/mysql.js @@ -46,6 +46,10 @@ Mysql.prototype.visitOnDuplicate = function(onDuplicate) { 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.'); }; diff --git a/lib/dialect/oracle.js b/lib/dialect/oracle.js index ea2cb85e..d791b8f7 100644 --- a/lib/dialect/oracle.js +++ b/lib/dialect/oracle.js @@ -224,6 +224,9 @@ Oracle.prototype.visitCase = function(caseExp) { return Mssql.prototype.visitCase.call(this, caseExp); }; +Oracle.prototype.visitOnConflict = function(onConflict) { + throw new Error('Oracle does not allow onConflict clause.'); +}; function isCreateIfNotExists(create){ if (create.nodes.length===0) return false; @@ -253,4 +256,5 @@ function isCountStarExpression(columnNode){ return true; } + module.exports = Oracle; diff --git a/lib/dialect/postgres.js b/lib/dialect/postgres.js index a57e6402..a55f343d 100644 --- a/lib/dialect/postgres.js +++ b/lib/dialect/postgres.js @@ -149,6 +149,7 @@ Postgres.prototype.visit = function(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); @@ -1037,6 +1038,36 @@ 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; + + 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(onConflict.columns[i])); + } + result.push( '(' + columns.join(', ') + ')' ); + } + + if(onConflict.update){ + updateClause.push("DO UPDATE SET"); + var update = onConflict.update; + var setClause = []; + for(i=0; i Date: Wed, 19 Oct 2016 09:57:45 -0500 Subject: [PATCH 119/153] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 64e2f317..6f2b9d02 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "author": "brianc ", "name": "sql", "description": "sql builder", - "version": "0.71.0", + "version": "0.72.0", "homepage": "https://github.com/brianc/node-sql", "license": "MIT", "repository": { From cc897ca79340758f1f10f30ef847d496d781e22e Mon Sep 17 00:00:00 2001 From: Rohit Singh Date: Tue, 20 Dec 2016 19:45:49 +0100 Subject: [PATCH 120/153] =?UTF-8?q?the=20ON=20CONFLICT=20feature=20for=20#?= =?UTF-8?q?PostgreSQL=20was=20missing=20camelcase=20support=E2=80=A6=20(#3?= =?UTF-8?q?42)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * the ON CONFLICT feature for #PostgreSQL was missing camelcase support of column names this commit fixes that and takes care of issue #341 it now honours snakeToCamel set to true * we can quote the column name only once and save extra operation --- lib/dialect/postgres.js | 9 ++-- test/dialects/insert-tests.js | 83 +++++++++++++++++++++++++++++++++++ test/dialects/support.js | 9 ++++ 3 files changed, 97 insertions(+), 4 deletions(-) diff --git a/lib/dialect/postgres.js b/lib/dialect/postgres.js index a55f343d..9d9f547a 100644 --- a/lib/dialect/postgres.js +++ b/lib/dialect/postgres.js @@ -1041,13 +1041,13 @@ Postgres.prototype.visitOnDuplicate = function(onDuplicate) { Postgres.prototype.visitOnConflict = function(onConflict) { var result = ['ON CONFLICT']; var columns = []; - var updateClause = [], i; - + 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(onConflict.columns[i])); + columns.push(this.quote(table.getColumn(onConflict.columns[i]).name)); } result.push( '(' + columns.join(', ') + ')' ); } @@ -1057,7 +1057,8 @@ Postgres.prototype.visitOnConflict = function(onConflict) { var update = onConflict.update; var setClause = []; for(i=0; i Date: Tue, 20 Dec 2016 13:46:06 -0500 Subject: [PATCH 121/153] Fix adding/dropping columns with oracle. (#338) --- lib/dialect/oracle.js | 61 ++++++++++++++++++++++++++++++ test/dialects/alter-table-tests.js | 32 ++++++++++++++-- 2 files changed, 89 insertions(+), 4 deletions(-) diff --git a/lib/dialect/oracle.js b/lib/dialect/oracle.js index d791b8f7..9a9f6caa 100644 --- a/lib/dialect/oracle.js +++ b/lib/dialect/oracle.js @@ -28,6 +28,56 @@ Oracle.prototype.visitAlias = function(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 Date: Tue, 20 Dec 2016 13:46:21 -0500 Subject: [PATCH 122/153] Fix issue when calling insert with an object that has a length property instead of an array. (#337) --- lib/table.js | 2 +- test/dialects/insert-tests.js | 25 +++++++++++++++++++++++++ test/dialects/support.js | 2 +- 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/lib/table.js b/lib/table.js index 21895aea..0b75c5fe 100644 --- a/lib/table.js +++ b/lib/table.js @@ -208,7 +208,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 { diff --git a/test/dialects/insert-tests.js b/test/dialects/insert-tests.js index 30f83712..aca6be7b 100644 --- a/test/dialects/insert-tests.js +++ b/test/dialects/insert-tests.js @@ -57,6 +57,31 @@ Harness.test({ 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', diff --git a/test/dialects/support.js b/test/dialects/support.js index 83da378f..4a9fa427 100644 --- a/test/dialects/support.js +++ b/test/dialects/support.js @@ -76,7 +76,7 @@ module.exports = { definePostTable: function() { return Table.define({ name: 'post', - columns: ['id', 'userId', 'content', 'tags'] + columns: ['id', 'userId', 'content', 'tags', 'length'] }); }, From 1049cd7508d0ff86cb6fc314d6eaba89ad8ae98a Mon Sep 17 00:00:00 2001 From: petrovi4 Date: Tue, 20 Dec 2016 21:46:43 +0300 Subject: [PATCH 123/153] Added value expression for ? operator "contain key" (#334) --- lib/node/valueExpression.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/node/valueExpression.js b/lib/node/valueExpression.js index faad8033..2fa741a9 100644 --- a/lib/node/valueExpression.js +++ b/lib/node/valueExpression.js @@ -148,6 +148,7 @@ var ValueExpressionMixin = function() { at : atMethod, contains : binaryMethod('@>'), containedBy : binaryMethod('<@'), + containsKey : binaryMethod('?'), overlap : binaryMethod('&&'), slice : sliceMethod, cast : castMethod, From 72aff9012a3f4339892d6283a6b872d682faef11 Mon Sep 17 00:00:00 2001 From: Kevin Anthoney Date: Tue, 20 Dec 2016 18:47:03 +0000 Subject: [PATCH 124/153] Add date time interval functionality to postgres and mysql dialects (#326) * Add date time interval functionality to postgres and mysql dialects * Add date interval test involving years and months --- lib/dialect/mysql.js | 27 +++++++++++ lib/dialect/postgres.js | 21 +++++++++ lib/index.js | 26 ++++++---- lib/node/interval.js | 21 +++++++++ test/dialects/date-tests.js | 94 ++++++++++++++++++++++++++++++++++++- 5 files changed, 178 insertions(+), 11 deletions(-) create mode 100644 lib/node/interval.js diff --git a/lib/dialect/mysql.js b/lib/dialect/mysql.js index 4d054564..5212ba5b 100644 --- a/lib/dialect/mysql.js +++ b/lib/dialect/mysql.js @@ -2,6 +2,7 @@ var util = require('util'); var assert = require('assert'); +var _ = require('lodash'); var Mysql = function(config) { this.output = []; @@ -155,4 +156,30 @@ Mysql.prototype.visitColumn = function(columnNode) { 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/postgres.js b/lib/dialect/postgres.js index 9d9f547a..6520e1cc 100644 --- a/lib/dialect/postgres.js +++ b/lib/dialect/postgres.js @@ -174,6 +174,7 @@ Postgres.prototype.visit = function(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); @@ -1139,6 +1140,26 @@ Postgres.prototype.visitCreateView = function(createView) { return result; }; +Postgres.prototype.visitInterval = function(interval) { + var parameter = ''; + function _add(n, unit) { + if(!_.isNumber(n)) return; + if(parameter !== '') { + parameter += ' '; + } + parameter += n + ' ' + unit; + } + _add(interval.years, 'YEAR'); + _add(interval.months, 'MONTH'); + _add(interval.days, 'DAY'); + _add(interval.hours, 'HOUR'); + _add(interval.minutes, 'MINUTE'); + _add(interval.seconds, 'SECOND'); + if(parameter === '') parameter = '0 SECOND'; + var result = "INTERVAL '" + parameter + "'"; + return result; +}; + /** * Broken out as a separate function so that dialects that derive from this class can still use this functionality. * diff --git a/lib/index.js b/lib/index.js index 1309fcf7..3a1cdfdc 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,15 +1,15 @@ '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 ParameterNode = require('./node/parameter'); -var Query = require('./node/query'); -var sliced = require('sliced'); -var Table = require('./table'); +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 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'; @@ -51,6 +51,12 @@ Sql.prototype.select = function() { return query; }; +// Returns an interval clause +Sql.prototype.interval = function() { + var interval = new Interval(sliced(arguments)); + return interval; +}; + // Set the dialect Sql.prototype.setDialect = function(dialect, config) { this.dialect = getDialect(dialect); diff --git a/lib/node/interval.js b/lib/node/interval.js new file mode 100644 index 00000000..231e0d0d --- /dev/null +++ b/lib/node/interval.js @@ -0,0 +1,21 @@ +'use strict'; + +var Node = require(__dirname); +var ParameterNode = require(__dirname + '/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/test/dialects/date-tests.js b/test/dialects/date-tests.js index 5a5fadda..dd2c4d64 100644 --- a/test/dialects/date-tests.js +++ b/test/dialects/date-tests.js @@ -133,4 +133,96 @@ Harness.test({ string: 'SELECT CURRENT_TIMESTAMP FROM "customer"' }, params: [] -}); \ No newline at end of file +}); + +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: [] +}); + From f941c74561e8a1fe31ca0baec4a9ffbbb7cac6f4 Mon Sep 17 00:00:00 2001 From: Christian Schuhmann Date: Tue, 20 Dec 2016 19:47:27 +0100 Subject: [PATCH 125/153] Fix Table.hasColumn for columns with custom property names (#322) There was a bug which caused Table.hasColumn to return false when passed a property name (defined via column.property) instead of a column name. --- lib/table.js | 9 +++------ test/table-tests.js | 13 +++++++++++++ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/lib/table.js b/lib/table.js index 0b75c5fe..0f657236 100644 --- a/lib/table.js +++ b/lib/table.js @@ -126,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 = diff --git a/test/table-tests.js b/test/table-tests.js index 66c3bdd3..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'); From 2808409da82cd4d5bda5577a64de5f11dcb43a62 Mon Sep 17 00:00:00 2001 From: Kevin Anthoney Date: Tue, 20 Dec 2016 18:47:57 +0000 Subject: [PATCH 126/153] add defaultValue parameter when creating tables (#311) --- lib/column.js | 1 + lib/dialect/postgres.js | 3 +++ lib/node/column.js | 1 + test/dialects/create-table-tests.js | 33 +++++++++++++++++++++++++++++ 4 files changed, 38 insertions(+) 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/postgres.js b/lib/dialect/postgres.js index 6520e1cc..20c4edab 100644 --- a/lib/dialect/postgres.js +++ b/lib/dialect/postgres.js @@ -826,6 +826,9 @@ Postgres.prototype.visitColumn = function(columnNode) { if (!columnNode.primaryKey && columnNode.unique) { txt.push(' UNIQUE'); } + if (columnNode.defaultValue !== undefined) { + txt.push(' DEFAULT ' + this._getParameterValue(columnNode.defaultValue)); + } } if (!!columnNode.references) { diff --git a/lib/node/column.js b/lib/node/column.js index 6a5f7284..3fda47b0 100644 --- a/lib/node/column.js +++ b/lib/node/column.js @@ -20,6 +20,7 @@ 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 diff --git a/test/dialects/create-table-tests.js b/test/dialects/create-table-tests.js index 97f6a1fd..b1150617 100644 --- a/test/dialects/create-table-tests.js +++ b/test/dialects/create-table-tests.js @@ -248,6 +248,39 @@ Harness.test({ } }); +Harness.test({ + query: Table.define({ + name: 'user', + columns: [{ + name: 'id', + dataType: 'int', + primaryKey: true, + notNull: true + }, { + name: 'posts', + dataType: 'int', + notNull: true, + defaultValue: 0 + }] + }).create(), + pg: { + text : 'CREATE TABLE "user" ("id" int PRIMARY KEY, "posts" int NOT NULL DEFAULT 0)', + string: 'CREATE TABLE "user" ("id" int PRIMARY KEY, "posts" int NOT NULL DEFAULT 0)' + }, + sqlite: { + text : 'CREATE TABLE "user" ("id" int PRIMARY KEY, "posts" int NOT NULL DEFAULT 0)', + string: 'CREATE TABLE "user" ("id" int PRIMARY KEY, "posts" int NOT NULL DEFAULT 0)' + }, + mysql: { + text : 'CREATE TABLE `user` (`id` int PRIMARY KEY, `posts` int NOT NULL DEFAULT 0)', + string: 'CREATE TABLE `user` (`id` int PRIMARY KEY, `posts` int NOT NULL DEFAULT 0)' + }, + oracle: { + text : 'CREATE TABLE "user" ("id" int PRIMARY KEY, "posts" int NOT NULL DEFAULT 0)', + string: 'CREATE TABLE "user" ("id" int PRIMARY KEY, "posts" int NOT NULL DEFAULT 0)' + } +}); + Harness.test({ query: Table.define({ name: 'post', From 4533b3636ef43ac2a0656ba05d4765ce41ddc6c0 Mon Sep 17 00:00:00 2001 From: Brad Dunbar Date: Tue, 20 Dec 2016 13:48:14 -0500 Subject: [PATCH 127/153] Check instanceof Date. (#310) Since we just need to know if this value is a Date, we can check it directly. --- lib/dialect/postgres.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/dialect/postgres.js b/lib/dialect/postgres.js index 20c4edab..9628f85d 100644 --- a/lib/dialect/postgres.js +++ b/lib/dialect/postgres.js @@ -59,9 +59,9 @@ Postgres.prototype._getParameterValue = function(value, quoteChar) { value = _.map(value, this._getParameterValue.bind(this)); value = '(' + value.join(', ') + ')'; } - } else if (_.isFunction(value.toISOString)) { + } 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')); From bc02f7850245964e15e63b4d904fa43114ec36a0 Mon Sep 17 00:00:00 2001 From: Brian Carlson Date: Tue, 20 Dec 2016 12:49:20 -0600 Subject: [PATCH 128/153] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6f2b9d02..2dc3a8e8 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "author": "brianc ", "name": "sql", "description": "sql builder", - "version": "0.72.0", + "version": "0.73.0", "homepage": "https://github.com/brianc/node-sql", "license": "MIT", "repository": { From 73bf26079feabb31d6f2258b58d0abb47e95fd00 Mon Sep 17 00:00:00 2001 From: Andy Katz Date: Wed, 8 Feb 2017 09:34:32 -0600 Subject: [PATCH 129/153] add OR IGNORE for sqlite (#347) * add OR IGNORE for sqlite * add missing semicolon --- lib/dialect/postgres.js | 14 +++++++++----- lib/dialect/sqlite.js | 4 ++++ lib/node/orIgnore.js | 7 +++++++ lib/node/query.js | 6 ++++++ test/dialects/insert-tests.js | 24 ++++++++++++++++++++++++ 5 files changed, 50 insertions(+), 5 deletions(-) create mode 100644 lib/node/orIgnore.js diff --git a/lib/dialect/postgres.js b/lib/dialect/postgres.js index 9628f85d..6e67c38e 100644 --- a/lib/dialect/postgres.js +++ b/lib/dialect/postgres.js @@ -162,6 +162,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); @@ -237,11 +238,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(); @@ -995,6 +995,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']; }; diff --git a/lib/dialect/sqlite.js b/lib/dialect/sqlite.js index 0cd31335..fb2450d4 100644 --- a/lib/dialect/sqlite.js +++ b/lib/dialect/sqlite.js @@ -165,4 +165,8 @@ Sqlite.prototype.visitBinary = function(binary) { return Sqlite.super_.prototype.visitBinary.call(this, binary); }; +Sqlite.prototype.visitOrIgnore = function() { + return ['OR IGNORE']; +}; + module.exports = Sqlite; diff --git a/lib/node/orIgnore.js b/lib/node/orIgnore.js new file mode 100644 index 00000000..849750bc --- /dev/null +++ b/lib/node/orIgnore.js @@ -0,0 +1,7 @@ +'use strict'; + +var Node = require(__dirname); + +module.exports = Node.define({ + type: 'OR IGNORE' +}); diff --git a/lib/node/query.js b/lib/node/query.js index 9abc9b64..1927dbe9 100644 --- a/lib/node/query.js +++ b/lib/node/query.js @@ -37,6 +37,7 @@ 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'); @@ -468,6 +469,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; diff --git a/test/dialects/insert-tests.js b/test/dialects/insert-tests.js index aca6be7b..60ba6a62 100644 --- a/test/dialects/insert-tests.js +++ b/test/dialects/insert-tests.js @@ -680,6 +680,30 @@ Harness.test({ 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', From 6d20ddd55e26c8d43032d35b874c87614cae13e8 Mon Sep 17 00:00:00 2001 From: Brian Carlson Date: Wed, 8 Feb 2017 09:34:43 -0600 Subject: [PATCH 130/153] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2dc3a8e8..6b984199 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "author": "brianc ", "name": "sql", "description": "sql builder", - "version": "0.73.0", + "version": "0.74.0", "homepage": "https://github.com/brianc/node-sql", "license": "MIT", "repository": { From 1af2a33c4b69972de90df7e4cf8f535753d3098d Mon Sep 17 00:00:00 2001 From: Lorenzo Giuliani Date: Sat, 15 Apr 2017 00:59:11 +0200 Subject: [PATCH 131/153] use browserify-safe require statements (#352) all boils down to _not_ using `__dirname` inside a require call --- lib/dialect/mssql.js | 2 +- lib/dialect/mysql.js | 2 +- lib/dialect/oracle.js | 4 ++-- lib/dialect/sqlite.js | 2 +- lib/functions.js | 2 +- lib/node/addColumn.js | 2 +- lib/node/alias.js | 2 +- lib/node/alter.js | 2 +- lib/node/arrayCall.js | 8 ++++---- lib/node/at.js | 6 +++--- lib/node/binary.js | 6 +++--- lib/node/cascade.js | 2 +- lib/node/case.js | 6 +++--- lib/node/cast.js | 6 +++--- lib/node/column.js | 2 +- lib/node/create.js | 2 +- lib/node/createView.js | 2 +- lib/node/default.js | 2 +- lib/node/delete.js | 2 +- lib/node/distinct.js | 2 +- lib/node/distinctOn.js | 2 +- lib/node/drop.js | 2 +- lib/node/dropColumn.js | 2 +- lib/node/forShare.js | 2 +- lib/node/forUpdate.js | 2 +- lib/node/foreignKey.js | 2 +- lib/node/from.js | 2 +- lib/node/functionCall.js | 8 ++++---- lib/node/groupBy.js | 2 +- lib/node/having.js | 2 +- lib/node/ifExists.js | 2 +- lib/node/ifNotExists.js | 2 +- lib/node/in.js | 6 +++--- lib/node/index.js | 2 +- lib/node/interval.js | 4 ++-- lib/node/join.js | 2 +- lib/node/literal.js | 2 +- lib/node/notIn.js | 6 +++--- lib/node/onConflict.js | 2 +- lib/node/onDuplicate.js | 2 +- lib/node/orIgnore.js | 2 +- lib/node/orderBy.js | 2 +- lib/node/orderByValue.js | 2 +- lib/node/parameter.js | 2 +- lib/node/postfixUnary.js | 6 +++--- lib/node/prefixUnary.js | 6 +++--- lib/node/query.js | 4 ++-- lib/node/rename.js | 2 +- lib/node/renameColumn.js | 2 +- lib/node/restrict.js | 2 +- lib/node/returning.js | 2 +- lib/node/select.js | 2 +- lib/node/slice.js | 6 +++--- lib/node/table.js | 2 +- lib/node/ternary.js | 6 +++--- lib/node/text.js | 2 +- lib/node/truncate.js | 2 +- lib/node/update.js | 2 +- lib/node/where.js | 6 +++--- lib/table.js | 14 +++++++------- 60 files changed, 97 insertions(+), 97 deletions(-) diff --git a/lib/dialect/mssql.js b/lib/dialect/mssql.js index e93fc45a..3606f5b6 100644 --- a/lib/dialect/mssql.js +++ b/lib/dialect/mssql.js @@ -20,7 +20,7 @@ var Mssql = function(config) { this.config = config || {}; }; -var Postgres = require(__dirname + '/postgres'); +var Postgres = require('./postgres'); util.inherits(Mssql, Postgres); diff --git a/lib/dialect/mysql.js b/lib/dialect/mysql.js index 5212ba5b..b2fc2c26 100644 --- a/lib/dialect/mysql.js +++ b/lib/dialect/mysql.js @@ -10,7 +10,7 @@ var Mysql = function(config) { this.config = config || {}; }; -var Postgres = require(__dirname + '/postgres'); +var Postgres = require('./postgres'); util.inherits(Mysql, Postgres); diff --git a/lib/dialect/oracle.js b/lib/dialect/oracle.js index 9a9f6caa..b1f95eec 100644 --- a/lib/dialect/oracle.js +++ b/lib/dialect/oracle.js @@ -9,9 +9,9 @@ var Oracle = function(config) { this.config = config || {}; }; -var Postgres = require(__dirname + '/postgres'); +var Postgres = require('./postgres'); -var Mssql = require(__dirname + '/mssql'); +var Mssql = require('./mssql'); util.inherits(Oracle, Postgres); diff --git a/lib/dialect/sqlite.js b/lib/dialect/sqlite.js index fb2450d4..af2c3ef4 100644 --- a/lib/dialect/sqlite.js +++ b/lib/dialect/sqlite.js @@ -11,7 +11,7 @@ var Sqlite = function(config) { this.config = config || {}; }; -var Postgres = require(__dirname + '/postgres'); +var Postgres = require('./postgres'); util.inherits(Sqlite, Postgres); diff --git a/lib/functions.js b/lib/functions.js index b0abcf54..4362b706 100644 --- a/lib/functions.js +++ b/lib/functions.js @@ -1,7 +1,7 @@ '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) { 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 3fda47b0..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', diff --git a/lib/node/create.js b/lib/node/create.js index c048c3ce..67cd9b3d 100644 --- a/lib/node/create.js +++ b/lib/node/create.js @@ -1,6 +1,6 @@ 'use strict'; -var Node = require(__dirname); +var Node = require('./index'); module.exports = Node.define({ type: 'CREATE', diff --git a/lib/node/createView.js b/lib/node/createView.js index bc52684f..7d456d90 100644 --- a/lib/node/createView.js +++ b/lib/node/createView.js @@ -1,6 +1,6 @@ 'use strict'; -var Node = require(__dirname); +var Node = require('./index'); module.exports = Node.define({ type: 'CREATE VIEW', 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 index a115f148..8c681551 100644 --- a/lib/node/distinct.js +++ b/lib/node/distinct.js @@ -1,6 +1,6 @@ 'use strict'; -var Node = require(__dirname); +var Node = require('./index'); module.exports = Node.define({ type: 'DISTINCT', diff --git a/lib/node/distinctOn.js b/lib/node/distinctOn.js index a208dfde..72c430e8 100644 --- a/lib/node/distinctOn.js +++ b/lib/node/distinctOn.js @@ -1,6 +1,6 @@ 'use strict'; -var Node = require(__dirname); +var Node = require('./index'); module.exports = Node.define({ type: 'DISTINCT ON', diff --git a/lib/node/drop.js b/lib/node/drop.js index 177b6d1a..b0428347 100644 --- a/lib/node/drop.js +++ b/lib/node/drop.js @@ -1,6 +1,6 @@ 'use strict'; -var Node = require(__dirname); +var Node = require('./index'); module.exports = Node.define({ type: 'DROP', 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 index c738d842..a0ad99c1 100644 --- a/lib/node/foreignKey.js +++ b/lib/node/foreignKey.js @@ -1,6 +1,6 @@ 'use strict'; -var Node = require(__dirname); +var Node = require('./index'); module.exports = Node.define({ type: 'FOREIGN KEY', 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 d519466f..66c0e481 100644 --- a/lib/node/index.js +++ b/lib/node/index.js @@ -98,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 index 231e0d0d..9f46f9ab 100644 --- a/lib/node/interval.js +++ b/lib/node/interval.js @@ -1,7 +1,7 @@ 'use strict'; -var Node = require(__dirname); -var ParameterNode = require(__dirname + '/parameter'); +var Node = require('./index'); +var ParameterNode = require('./parameter'); var IntervalNode = Node.define({ type: 'INTERVAL', diff --git a/lib/node/join.js b/lib/node/join.js index 7186f694..bbc1f93a 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) { 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 index cd785120..cc71ac47 100644 --- a/lib/node/onConflict.js +++ b/lib/node/onConflict.js @@ -1,6 +1,6 @@ 'use strict'; -var Node = require(__dirname); +var Node = require('./index'); module.exports = Node.define({ type: 'ONCONFLICT' diff --git a/lib/node/onDuplicate.js b/lib/node/onDuplicate.js index 38146793..cd653cb3 100644 --- a/lib/node/onDuplicate.js +++ b/lib/node/onDuplicate.js @@ -1,6 +1,6 @@ 'use strict'; -var Node = require(__dirname); +var Node = require('./index'); module.exports = Node.define({ type: 'ONDUPLICATE' diff --git a/lib/node/orIgnore.js b/lib/node/orIgnore.js index 849750bc..e23c9d6d 100644 --- a/lib/node/orIgnore.js +++ b/lib/node/orIgnore.js @@ -1,6 +1,6 @@ 'use strict'; -var Node = require(__dirname); +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 c2276710..051ed852 100644 --- a/lib/node/parameter.js +++ b/lib/node/parameter.js @@ -1,6 +1,6 @@ 'use strict'; -var Node = require(__dirname); +var Node = require('./index'); var ParameterNode = module.exports = Node.define({ type: 'PARAMETER', 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 1927dbe9..674d07b7 100644 --- a/lib/node/query.js +++ b/lib/node/query.js @@ -1,11 +1,11 @@ 'use strict'; var _ = require('lodash'); -var alias = require(__dirname + '/alias'); +var alias = require('./alias'); var assert = require('assert'); var sliced = require('sliced'); var util = require('util'); -var valueExpressionMixin = require(__dirname + '/valueExpression'); +var valueExpressionMixin = require('./valueExpression'); var Node = require('./'); var Select = require('./select'); 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/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 a485b814..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', 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 index 790c26ca..165e0d9e 100644 --- a/lib/node/truncate.js +++ b/lib/node/truncate.js @@ -1,6 +1,6 @@ 'use strict'; -var Node = require(__dirname); +var Node = require('./index'); module.exports = Node.define({ type: 'TRUNCATE', 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/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 0f657236..23e335a8 100644 --- a/lib/table.js +++ b/lib/table.js @@ -3,13 +3,13 @@ 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 ForeignKeyNode = require(__dirname + '/node/foreignKey'); +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; From 7187f755da8dcd08a525ca3c9f5cf8e119288b11 Mon Sep 17 00:00:00 2001 From: "Brian M. Carlson" Date: Fri, 14 Apr 2017 17:59:50 -0500 Subject: [PATCH 132/153] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6b984199..8a61259b 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "author": "brianc ", "name": "sql", "description": "sql builder", - "version": "0.74.0", + "version": "0.75.0", "homepage": "https://github.com/brianc/node-sql", "license": "MIT", "repository": { From 6defdca12271765a3aef057a94241527aa6bad1e Mon Sep 17 00:00:00 2001 From: Santiago Castro Date: Mon, 17 Apr 2017 13:42:29 -0300 Subject: [PATCH 133/153] Fix broken Markdown headings (#353) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 193ab2be..e1a3af8c 100644 --- a/README.md +++ b/README.md @@ -183,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 From f9420542eddbb3479b21f635644617de7629e4b5 Mon Sep 17 00:00:00 2001 From: 3n-mb <3n-mb@users.noreply.github.com> Date: Tue, 23 May 2017 10:25:04 -0400 Subject: [PATCH 134/153] Adding types v1 (#357) * Create types.d.ts * Add types field --- lib/types.d.ts | 211 +++++++++++++++++++++++++++++++++++++++++++++++++ package.json | 1 + 2 files changed, 212 insertions(+) create mode 100644 lib/types.d.ts diff --git a/lib/types.d.ts b/lib/types.d.ts new file mode 100644 index 00000000..d75e59e8 --- /dev/null +++ b/lib/types.d.ts @@ -0,0 +1,211 @@ + +/** + * 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" { + + 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]: ColumnDefinition}; + 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; + +} diff --git a/package.json b/package.json index 8a61259b..be22764f 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "url": "git://github.com/brianc/node-sql.git" }, "main": "lib/", + "types": "lib/types.d.ts", "scripts": { "test": "node_modules/.bin/mocha", "lint": "jshint lib test", From 0da386cec5348b5816688493ba36b68cd0600ce1 Mon Sep 17 00:00:00 2001 From: "Brian M. Carlson" Date: Tue, 23 May 2017 11:57:06 -0500 Subject: [PATCH 135/153] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index be22764f..64cbac97 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "author": "brianc ", "name": "sql", "description": "sql builder", - "version": "0.75.0", + "version": "0.76.0", "homepage": "https://github.com/brianc/node-sql", "license": "MIT", "repository": { From 3688b020749be9e0db8f8290ffcb284e447b06d7 Mon Sep 17 00:00:00 2001 From: Lorenzo Giuliani Date: Thu, 1 Jun 2017 21:10:58 +0200 Subject: [PATCH 136/153] add missing function `setDialect` (#359) add a new type definition: SQLDialects as Union of strings. --- lib/types.d.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/types.d.ts b/lib/types.d.ts index d75e59e8..84fb7ab3 100644 --- a/lib/types.d.ts +++ b/lib/types.d.ts @@ -6,6 +6,14 @@ */ declare module "sql" { + type SQLDialects = + | "mssql" + | "mysql" + | "oracle" + | "postgres" + | "sqlite" + ; + interface OrderByValueNode {} interface Named { @@ -30,6 +38,7 @@ declare module "sql" { name: Name; schema: string; columns: {[CName in keyof Row]: ColumnDefinition}; + dialect?: SQLDialects; isTemporary?: boolean; foreignKeys?: { table: string, @@ -207,5 +216,6 @@ declare module "sql" { } function define(map:TableDefinition): Table; + function setDialect(dialect: SQLDialects): void; } From f135dfc6ed49cbcf3dba31bcd5a02c815aa2a0f5 Mon Sep 17 00:00:00 2001 From: Brian Carlson Date: Thu, 1 Jun 2017 14:11:31 -0500 Subject: [PATCH 137/153] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 64cbac97..b7c9b141 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "author": "brianc ", "name": "sql", "description": "sql builder", - "version": "0.76.0", + "version": "0.76.1", "homepage": "https://github.com/brianc/node-sql", "license": "MIT", "repository": { From 1fbaf62b18c20651f2de6d53c2fd7828e9c49987 Mon Sep 17 00:00:00 2001 From: Luke Childs Date: Mon, 31 Jul 2017 15:00:44 +0100 Subject: [PATCH 138/153] Support REPLACE (#368) * Support REPLACE * Add REPLACE to default Postgres dialect * Make sure REPLACE doesn't get prepended with SELECT * * Make sure REPLACE doesn't use table.column syntax * REPLACE throws on unsupported dialects * Move replace to dialect prototypes * Copy MySQL insert syntax for repalce * Run insert tests against replace * Fix misplaced semicolon * Treat subquery parenthesis the same on INSERT and REPLACE --- lib/dialect/mssql.js | 4 + lib/dialect/mysql.js | 36 ++ lib/dialect/oracle.js | 4 + lib/dialect/postgres.js | 10 +- lib/dialect/sqlite.js | 33 ++ lib/node/query.js | 31 + lib/node/replace.js | 70 +++ lib/table.js | 11 + test/dialects/replace-tests.js | 1000 ++++++++++++++++++++++++++++++++ 9 files changed, 1197 insertions(+), 2 deletions(-) create mode 100644 lib/node/replace.js create mode 100644 test/dialects/replace-tests.js diff --git a/lib/dialect/mssql.js b/lib/dialect/mssql.js index 3606f5b6..488f7fa2 100644 --- a/lib/dialect/mssql.js +++ b/lib/dialect/mssql.js @@ -30,6 +30,10 @@ 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; diff --git a/lib/dialect/mysql.js b/lib/dialect/mysql.js index b2fc2c26..0a63f3be 100644 --- a/lib/dialect/mysql.js +++ b/lib/dialect/mysql.js @@ -20,6 +20,42 @@ Mysql.prototype._quoteCharacter = '`'; Mysql.prototype._arrayAggFunctionName = 'GROUP_CONCAT'; +Mysql.prototype.visitReplace = function(replace) { + var self = this; + // don't use table.column for replaces + this._visitedReplace = true; + + var result = ['REPLACE']; + result = result.concat(replace.nodes.map(this.visit.bind(this))); + result.push('INTO ' + this.visit(this._queryNode.table.toNode())); + result.push('(' + replace.columns.map(this.visit.bind(this)).join(', ') + ')'); + + var paramNodes = replace.getParameters(); + + if (paramNodes.length > 0) { + var paramText = paramNodes.map(function (paramSet) { + return paramSet.map(function (param) { + return self.visit(param); + }).join(', '); + }).map(function (param) { + return '('+param+')'; + }).join(', '); + + result.push('VALUES', paramText); + + if (result.slice(2, 5).join(' ') === '() VALUES ()') { + result.splice(2, 3, 'DEFAULT VALUES'); + } + } + + this._visitedReplace = false; + + if (result[2] === 'DEFAULT VALUES') { + result[2] = '() VALUES ()'; + } + return result; +}; + Mysql.prototype._getParameterPlaceholder = function() { return '?'; }; diff --git a/lib/dialect/oracle.js b/lib/dialect/oracle.js index b1f95eec..38267339 100644 --- a/lib/dialect/oracle.js +++ b/lib/dialect/oracle.js @@ -17,6 +17,10 @@ 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 */ diff --git a/lib/dialect/postgres.js b/lib/dialect/postgres.js index 6e67c38e..2816c901 100644 --- a/lib/dialect/postgres.js +++ b/lib/dialect/postgres.js @@ -131,6 +131,7 @@ 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); @@ -266,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 = []; @@ -624,6 +629,7 @@ Postgres.prototype.visitQuery = function(queryNode) { break; case "INDEXES": case "INSERT": + case "REPLACE": case "UPDATE": case "CREATE": case "DROP": @@ -725,7 +731,7 @@ Postgres.prototype.visitTable = function(tableNode) { 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 || @@ -1211,7 +1217,7 @@ Postgres.prototype.handleDistinct = function(actions,filters) { function dontParenthesizeSubQuery(parentQuery){ if (!parentQuery) return false; if (parentQuery.nodes.length === 0) return false; - if (parentQuery.nodes[0].type != 'INSERT') return false; + if (['INSERT', 'REPLACE'].indexOf(parentQuery.nodes[0].type) === -1) return false; return true; } diff --git a/lib/dialect/sqlite.js b/lib/dialect/sqlite.js index af2c3ef4..60d46997 100644 --- a/lib/dialect/sqlite.js +++ b/lib/dialect/sqlite.js @@ -19,6 +19,39 @@ Sqlite.prototype._myClass = Sqlite; Sqlite.prototype._arrayAggFunctionName = 'GROUP_CONCAT'; +Sqlite.prototype.visitReplace = function(replace) { + var self = this; + // don't use table.column for replaces + this._visitedReplace = true; + + var result = ['REPLACE']; + result = result.concat(replace.nodes.map(this.visit.bind(this))); + result.push('INTO ' + this.visit(this._queryNode.table.toNode())); + result.push('(' + replace.columns.map(this.visit.bind(this)).join(', ') + ')'); + + var paramNodes = replace.getParameters(); + + if (paramNodes.length > 0) { + var paramText = paramNodes.map(function (paramSet) { + return paramSet.map(function (param) { + return self.visit(param); + }).join(', '); + }).map(function (param) { + return '('+param+')'; + }).join(', '); + + result.push('VALUES', paramText); + + if (result.slice(2, 5).join(' ') === '() VALUES ()') { + result.splice(2, 3, 'DEFAULT VALUES'); + } + } + + this._visitedReplace = false; + + return result; +}; + Sqlite.prototype._getParameterValue = function(value) { if (Buffer.isBuffer(value)) { value = 'x' + this._getParameterValue(value.toString('hex')); diff --git a/lib/node/query.js b/lib/node/query.js index 674d07b7..973267bc 100644 --- a/lib/node/query.js +++ b/lib/node/query.js @@ -15,6 +15,7 @@ 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'); @@ -224,6 +225,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(); 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/table.js b/lib/table.js index 23e335a8..3fae2710 100644 --- a/lib/table.js +++ b/lib/table.js @@ -214,6 +214,17 @@ Table.prototype.insert = function() { return query; }; +Table.prototype.replace = function() { + var query = new Query(this); + if(!arguments[0] || (util.isArray(arguments[0]) && arguments[0].length === 0)){ + query.select.call(query, this.star()); + query.where.apply(query,["1=2"]); + } else { + query.replace.apply(query, arguments); + } + return query; +}; + Table.prototype.toNode = function() { return new TableNode(this); }; 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: [] +}); From 9173f92ea375ef86b315163ae1c2cb094ea98a68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96rd=C3=B6gh=20L=C3=A1szl=C3=B3?= Date: Mon, 31 Jul 2017 16:01:10 +0200 Subject: [PATCH 139/153] Fix order method with empty array generates invalid SQL (#364) --- lib/node/query.js | 3 +++ test/dialects/order-tests.js | 28 +++++++++++++++++++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/lib/node/query.js b/lib/node/query.js index 973267bc..ebb3ecfa 100644 --- a/lib/node/query.js +++ b/lib/node/query.js @@ -173,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 { diff --git a/test/dialects/order-tests.js b/test/dialects/order-tests.js index 2c4803f5..a0813d8e 100644 --- a/test/dialects/order-tests.js +++ b/test/dialects/order-tests.js @@ -336,4 +336,30 @@ Harness.test({ string: 'SELECT "post"."content" FROM "post" ORDER BY "post"."content"' }, params: [] -}); \ No newline at end of file +}); + +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: [] +}); + From 9135b0bbfd44d5d5a326022096a83d78427598e3 Mon Sep 17 00:00:00 2001 From: "Brian M. Carlson" Date: Mon, 31 Jul 2017 09:02:20 -0500 Subject: [PATCH 140/153] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b7c9b141..36ce3057 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "author": "brianc ", "name": "sql", "description": "sql builder", - "version": "0.76.1", + "version": "0.77.0", "homepage": "https://github.com/brianc/node-sql", "license": "MIT", "repository": { From c7334f3e34006a8fd604eaf72f841c546db7751a Mon Sep 17 00:00:00 2001 From: hstanford <30746076+hstanford@users.noreply.github.com> Date: Sun, 6 Aug 2017 21:09:50 +0100 Subject: [PATCH 141/153] Exposes function creation to allow use of unsupported functions (#371) --- lib/functions.js | 4 ++++ lib/index.js | 1 + test/function-tests.js | 6 ++++++ 3 files changed, 11 insertions(+) diff --git a/lib/functions.js b/lib/functions.js index 4362b706..fe9cdaaf 100644 --- a/lib/functions.js +++ b/lib/functions.js @@ -13,6 +13,9 @@ var getFunctionCallCreator = function(name) { // creates a hash of functions for a sql instance var getFunctions = function(functionNames) { + if (typeof functionNames === 'string') + return getFunctionCallCreator(functionNames); + var functions = _.reduce(functionNames, function(reducer, name) { reducer[name] = getFunctionCallCreator(name); return reducer; @@ -68,4 +71,5 @@ 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 3a1cdfdc..f6cf39e6 100644 --- a/lib/index.js +++ b/lib/index.js @@ -19,6 +19,7 @@ var Sql = function(dialect, config) { // attach the standard SQL functions to this instance this.functions = functions.getStandardFunctions(); + this.function = functions.getFunctions; }; // Define a table diff --git a/test/function-tests.js b/test/function-tests.js index 77ef0087..6c53072d 100644 --- a/test/function-tests.js +++ b/test/function-tests.js @@ -84,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'); + }); }); From 95d775a9a10650c2de34edf23a1a859abf7744dc Mon Sep 17 00:00:00 2001 From: "Brian M. Carlson" Date: Sun, 6 Aug 2017 15:10:02 -0500 Subject: [PATCH 142/153] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 36ce3057..dc250c01 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "author": "brianc ", "name": "sql", "description": "sql builder", - "version": "0.77.0", + "version": "0.78.0", "homepage": "https://github.com/brianc/node-sql", "license": "MIT", "repository": { From 6916535a96633548d5da31d8b74890ce2f087513 Mon Sep 17 00:00:00 2001 From: Dan Rzeppa Date: Sat, 9 Jun 2018 11:55:40 -0400 Subject: [PATCH 143/153] - Support for "left join lateral" in Postgres which converts to "outer apply" in Microsoft SQL Server and Oracle. --- lib/dialect/mssql.js | 10 +++++++ lib/dialect/oracle.js | 5 ++++ lib/dialect/postgres.js | 11 ++++++++ lib/node/join.js | 3 +++ lib/table.js | 4 +++ test/dialects/join-tests.js | 54 +++++++++++++++++++++++++++++++++++++ 6 files changed, 87 insertions(+) diff --git a/lib/dialect/mssql.js b/lib/dialect/mssql.js index 488f7fa2..6a19b564 100644 --- a/lib/dialect/mssql.js +++ b/lib/dialect/mssql.js @@ -274,6 +274,16 @@ Mssql.prototype.visitFunctionCall = function(functionCall) { 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; diff --git a/lib/dialect/oracle.js b/lib/dialect/oracle.js index 38267339..a55e7aca 100644 --- a/lib/dialect/oracle.js +++ b/lib/dialect/oracle.js @@ -278,6 +278,11 @@ 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.'); }; diff --git a/lib/dialect/postgres.js b/lib/dialect/postgres.js index 2816c901..87c7c9e9 100644 --- a/lib/dialect/postgres.js +++ b/lib/dialect/postgres.js @@ -1022,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)); @@ -1032,6 +1033,16 @@ 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) { diff --git a/lib/node/join.js b/lib/node/join.js index bbc1f93a..f569997e 100644 --- a/lib/node/join.js +++ b/lib/node/join.js @@ -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/table.js b/lib/table.js index 3fae2710..1408542b 100644 --- a/lib/table.js +++ b/lib/table.js @@ -237,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); diff --git a/test/dialects/join-tests.js b/test/dialects/join-tests.js index abebdb01..2e0c6882 100644 --- a/test/dialects/join-tests.js +++ b/test/dialects/join-tests.js @@ -174,3 +174,57 @@ Harness.test({ }, params: [] }); + +Harness.test({ + query: user.select().from(user.leftJoinLateral(post.subQuery().select(post.userId))), + pg: { + text : 'SELECT "user".* FROM "user" LEFT JOIN LATERAL (SELECT "post"."userId" FROM "post") ON true', + string: 'SELECT "user".* FROM "user" LEFT JOIN LATERAL (SELECT "post"."userId" FROM "post") ON true' + }, + mssql: { + text : 'SELECT [user].* FROM [user] OUTER APPLY (SELECT [post].[userId] FROM [post])', + string: 'SELECT [user].* FROM [user] OUTER APPLY (SELECT [post].[userId] FROM [post])' + }, + oracle: { + text : 'SELECT "user".* FROM "user" OUTER APPLY (SELECT "post"."userId" FROM "post")', + string: 'SELECT "user".* FROM "user" OUTER APPLY (SELECT "post"."userId" FROM "post")' + }, + params: [] +}); + +Harness.test({ + query: user.select().from(user.leftJoinLateral(post.subQuery().select(post.userId).where(user.id.equals(post.userId)))), + pg: { + text : 'SELECT "user".* FROM "user" LEFT JOIN LATERAL (SELECT "post"."userId" FROM "post" WHERE ("user"."id" = "post"."userId")) ON true', + string: 'SELECT "user".* FROM "user" LEFT JOIN LATERAL (SELECT "post"."userId" FROM "post" WHERE ("user"."id" = "post"."userId")) ON true' + }, + mssql: { + text : 'SELECT [user].* FROM [user] OUTER APPLY (SELECT [post].[userId] FROM [post] WHERE ([user].[id] = [post].[userId]))', + string: 'SELECT [user].* FROM [user] OUTER APPLY (SELECT [post].[userId] FROM [post] WHERE ([user].[id] = [post].[userId]))' + }, + oracle: { + text : 'SELECT "user".* FROM "user" OUTER APPLY (SELECT "post"."userId" FROM "post" WHERE ("user"."id" = "post"."userId"))', + string: 'SELECT "user".* FROM "user" OUTER APPLY (SELECT "post"."userId" FROM "post" WHERE ("user"."id" = "post"."userId"))' + }, + params: [] +}); + +Harness.test({ + query: user.select().from(user + .leftJoinLateral(post.subQuery().select(post.userId)) + .leftJoinLateral(comment.subQuery().select(comment.postId))), + pg: { + text : 'SELECT "user".* FROM "user" LEFT JOIN LATERAL (SELECT "post"."userId" FROM "post") ON true LEFT JOIN LATERAL (SELECT "comment"."postId" FROM "comment") ON true', + string: 'SELECT "user".* FROM "user" LEFT JOIN LATERAL (SELECT "post"."userId" FROM "post") ON true LEFT JOIN LATERAL (SELECT "comment"."postId" FROM "comment") ON true' + }, + mssql: { + text : 'SELECT [user].* FROM [user] OUTER APPLY (SELECT [post].[userId] FROM [post]) OUTER APPLY (SELECT [comment].[postId] FROM [comment])', + string: 'SELECT [user].* FROM [user] OUTER APPLY (SELECT [post].[userId] FROM [post]) OUTER APPLY (SELECT [comment].[postId] FROM [comment])' + }, + oracle: { + text : 'SELECT "user".* FROM "user" OUTER APPLY (SELECT "post"."userId" FROM "post") OUTER APPLY (SELECT "comment"."postId" FROM "comment")', + string: 'SELECT "user".* FROM "user" OUTER APPLY (SELECT "post"."userId" FROM "post") OUTER APPLY (SELECT "comment"."postId" FROM "comment")' + }, + params: [] +}); + From ed315d24da96e5ed332a3ffcb9d82dfadc808e3e Mon Sep 17 00:00:00 2001 From: Dan Rzeppa Date: Sat, 9 Jun 2018 12:08:55 -0400 Subject: [PATCH 144/153] - Override for Microsoft SQL Server concatenation operator. --- lib/dialect/mssql.js | 5 ++++ test/dialects/value-expression-tests.js | 34 ++++++++++++++++++++++--- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/lib/dialect/mssql.js b/lib/dialect/mssql.js index 488f7fa2..92229e06 100644 --- a/lib/dialect/mssql.js +++ b/lib/dialect/mssql.js @@ -48,6 +48,11 @@ Mssql.prototype.visitBinary = function(binary) { return [text]; } + if(binary.operator === '||'){ + var text = '(' + this.visit(binary.left) + ' + ' + this.visit(binary.right) + ')'; + return [text]; + } + if (!isRightSideArray(binary)){ return Mssql.super_.prototype.visitBinary.call(this, binary); } diff --git a/test/dialects/value-expression-tests.js b/test/dialects/value-expression-tests.js index 5eaae5ae..d5c7fcd4 100644 --- a/test/dialects/value-expression-tests.js +++ b/test/dialects/value-expression-tests.js @@ -114,20 +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\')))', + 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: [] +}); + From b0c1622ab53192b099db081b6bdbe58461a8d0f6 Mon Sep 17 00:00:00 2001 From: Dan Rzeppa Date: Sat, 9 Jun 2018 12:21:39 -0400 Subject: [PATCH 145/153] Tweak so that travis checks will pass --- lib/dialect/mssql.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/dialect/mssql.js b/lib/dialect/mssql.js index 92229e06..413ebd9b 100644 --- a/lib/dialect/mssql.js +++ b/lib/dialect/mssql.js @@ -49,8 +49,7 @@ Mssql.prototype.visitBinary = function(binary) { } if(binary.operator === '||'){ - var text = '(' + this.visit(binary.left) + ' + ' + this.visit(binary.right) + ')'; - return [text]; + return ['(' + this.visit(binary.left) + ' + ' + this.visit(binary.right) + ')']; } if (!isRightSideArray(binary)){ From 44f281f02ccda77b0a883fcc3a3c44c912f7f50d Mon Sep 17 00:00:00 2001 From: Dan Rzeppa Date: Sat, 9 Jun 2018 12:23:21 -0400 Subject: [PATCH 146/153] Added missing semicolon. --- lib/dialect/postgres.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/dialect/postgres.js b/lib/dialect/postgres.js index 87c7c9e9..9b81af25 100644 --- a/lib/dialect/postgres.js +++ b/lib/dialect/postgres.js @@ -1022,7 +1022,7 @@ Postgres.prototype.visitForShare = function() { }; Postgres.prototype.visitJoin = function(join) { - if (join.subType === 'LEFT LATERAL') return this.visitLeftJoinLateral(join) + if (join.subType === 'LEFT LATERAL') return this.visitLeftJoinLateral(join); var result = []; this._visitingJoin = true; result = result.concat(this.visit(join.from)); From 845e4cc2986041c5c3459975e345701e0e65c692 Mon Sep 17 00:00:00 2001 From: Dan Rzeppa Date: Sat, 9 Jun 2018 12:31:01 -0400 Subject: [PATCH 147/153] - Update ".travis.yml" to allow failures on Node 0.10 and 0.11 since the mocha library is doing stuff that isn't allowed. --- .travis.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.travis.yml b/.travis.yml index f04dabe6..07becfb2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,3 +4,8 @@ node_js: - "4.2" - "0.10" - "0.11" + +matrix: + allow_failures: + - node_js: "0.10" + - node_js: "0.11" \ No newline at end of file From 8e5a36ba8691659e4820ff8ee31b2c0507f548cf Mon Sep 17 00:00:00 2001 From: Dan Rzeppa Date: Sat, 9 Jun 2018 12:33:59 -0400 Subject: [PATCH 148/153] - Update ".travis.yml" to allow failures on Node 0.10 and 0.11 since the mocha library is doing stuff that isn't allowed. --- .travis.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.travis.yml b/.travis.yml index f04dabe6..07becfb2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,3 +4,8 @@ node_js: - "4.2" - "0.10" - "0.11" + +matrix: + allow_failures: + - node_js: "0.10" + - node_js: "0.11" \ No newline at end of file From 761469e033d35eefc1c97292be1b0bf58d61c317 Mon Sep 17 00:00:00 2001 From: Dan Rzeppa Date: Sat, 9 Jun 2018 13:27:58 -0400 Subject: [PATCH 149/153] Top-Level SubQuery support. --- lib/index.js | 12 ++++++++++++ test/dialects/subquery-tests.js | 26 ++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/lib/index.js b/lib/index.js index f6cf39e6..8e3d7cfa 100644 --- a/lib/index.js +++ b/lib/index.js @@ -52,6 +52,18 @@ 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)); diff --git a/test/dialects/subquery-tests.js b/test/dialects/subquery-tests.js index fe5b2aad..7d977b43 100644 --- a/test/dialects/subquery-tests.js +++ b/test/dialects/subquery-tests.js @@ -219,3 +219,29 @@ Harness.test({ 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: [] +}); + From c5c2e666acd8401a6c91a4a6c93704a4aaa4b954 Mon Sep 17 00:00:00 2001 From: Dan Rzeppa Date: Sat, 9 Jun 2018 13:30:38 -0400 Subject: [PATCH 150/153] Added test for aliased top-level subQuery --- test/dialects/subquery-tests.js | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/test/dialects/subquery-tests.js b/test/dialects/subquery-tests.js index 7d977b43..05d76f1f 100644 --- a/test/dialects/subquery-tests.js +++ b/test/dialects/subquery-tests.js @@ -245,3 +245,29 @@ Harness.test({ 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: [] +}); + From e8f45252690249c532efd0e7b77f57a8a6553cf7 Mon Sep 17 00:00:00 2001 From: Dan Rzeppa Date: Sat, 9 Jun 2018 13:37:09 -0400 Subject: [PATCH 151/153] Added missing require. --- lib/index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/index.js b/lib/index.js index 8e3d7cfa..f26ad827 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,11 +1,12 @@ 'use strict'; var _ = require('lodash'); -var Column = require("./column"); +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'); From 3b8de4721b2404f4dd9a904be2af722c95588a68 Mon Sep 17 00:00:00 2001 From: Dan Rzeppa Date: Sat, 9 Jun 2018 13:37:49 -0400 Subject: [PATCH 152/153] Update ".travis.yml" to allow failures on Node 0.10 and 0.11 since the mocha library is doing stuff that isn't allowed. --- .travis.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.travis.yml b/.travis.yml index f04dabe6..07becfb2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,3 +4,8 @@ node_js: - "4.2" - "0.10" - "0.11" + +matrix: + allow_failures: + - node_js: "0.10" + - node_js: "0.11" \ No newline at end of file From cc15b6720feefda627340838b986852546836130 Mon Sep 17 00:00:00 2001 From: 3n-mb <3n-mb@users.noreply.github.com> Date: Sat, 24 Aug 2024 16:44:34 -0400 Subject: [PATCH 153/153] Type change to allow typescript > 2.7.2 (#421) Typescript versions greater than 2.7.2, `keyof` produces `string|number|symbol`, failing on line 40. One fix is have on lines 19 and 22 `Name extends string|number|symbol` instead of `Name extends string`. But this work around may feel a bit not intuitive. Change directly on line 40, `&` with `string` does the trick of fitting types. --- lib/types.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/types.d.ts b/lib/types.d.ts index 84fb7ab3..a7011f99 100644 --- a/lib/types.d.ts +++ b/lib/types.d.ts @@ -37,7 +37,7 @@ declare module "sql" { interface TableDefinition { name: Name; schema: string; - columns: {[CName in keyof Row]: ColumnDefinition}; + columns: {[CName in ((keyof Row) & string)]: ColumnDefinition}; dialect?: SQLDialects; isTemporary?: boolean; foreignKeys?: {