Skip to content

Commit 1385c77

Browse files
committed
Add ON DUPLICATE KEY support for MySQL
1 parent 2987e13 commit 1385c77

7 files changed

Lines changed: 73 additions & 0 deletions

File tree

lib/dialect/mssql.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,10 @@ Mssql.prototype.visitQueryHelper=function(actions,targets,filters){
357357
// return "SHOW INDEX FROM " + tableName;
358358
//};
359359

360+
Mssql.prototype.visitOnDuplicate = function(onDuplicate) {
361+
throw new Error('MSSQL does not allow onDuplicate clause.');
362+
};
363+
360364
Mssql.prototype.visitReturning = function() {
361365
// TODO: need to add some code to the INSERT clause to support this since its the equivalent of the OUTPUT clause
362366
// in MS SQL which appears before the values, not at the end of the statement.

lib/dialect/mysql.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,20 @@ Mysql.prototype._getParameterValue = function(value) {
3131
return value;
3232
};
3333

34+
Mysql.prototype.visitOnDuplicate = function(onDuplicate) {
35+
var params = [];
36+
/* jshint boss: true */
37+
for(var i = 0, node; node = onDuplicate.nodes[i]; i++) {
38+
var target_col = this.visit(node);
39+
params = params.concat(target_col + ' = ' + this.visit(node.value));
40+
}
41+
var result = [
42+
'ON DUPLICATE KEY UPDATE',
43+
params.join(', ')
44+
];
45+
return result;
46+
};
47+
3448
Mysql.prototype.visitReturning = function() {
3549
throw new Error('MySQL does not allow returning clause.');
3650
};

lib/dialect/postgres.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ Postgres.prototype.visit = function(node) {
117117
case 'GROUP BY' : return this.visitGroupBy(node);
118118
case 'HAVING' : return this.visitHaving(node);
119119
case 'RETURNING' : return this.visitReturning(node);
120+
case 'ONDUPLICATE' : return this.visitOnDuplicate(node);
120121
case 'FOR UPDATE' : return this.visitForUpdate();
121122
case 'FOR SHARE' : return this.visitForShare();
122123
case 'TABLE' : return this.visitTable(node);
@@ -826,6 +827,10 @@ Postgres.prototype.visitReturning = function(returning) {
826827
return r;
827828
};
828829

830+
Postgres.prototype.visitOnDuplicate = function(onDuplicate) {
831+
throw new Error('PostgreSQL does not allow onDuplicate clause.');
832+
};
833+
829834
Postgres.prototype.visitModifier = function(node) {
830835
return [node.type, node.count.type ? this.visit(node.count) : node.count];
831836
};

lib/dialect/sqlite.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ Sqlite.prototype.visitRenameColumn = function() {
3838
throw new Error('SQLite does not allow renaming columns.');
3939
};
4040

41+
Sqlite.prototype.visitOnDuplicate = function() {
42+
throw new Error('SQLite does not allow onDuplicate clause.');
43+
};
44+
4145
Sqlite.prototype.visitReturning = function() {
4246
throw new Error('SQLite does not allow returning clause.');
4347
};

lib/node/onDuplicate.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
'use strict';
2+
3+
var Node = require(__dirname);
4+
5+
module.exports = Node.define({
6+
type: 'ONDUPLICATE'
7+
});

lib/node/query.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ var Insert = require('./insert');
1515
var Update = require('./update');
1616
var Delete = require('./delete');
1717
var Returning = require('./returning');
18+
var OnDuplicate = require('./onDuplicate');
1819
var ForUpdate = require('./forUpdate');
1920
var ForShare = require('./forShare');
2021
var Create = require('./create');
@@ -258,6 +259,21 @@ var Query = Node.define({
258259
return this.add(returning);
259260
},
260261

262+
onDuplicate: function(o) {
263+
var self = this;
264+
265+
var onDuplicate = new OnDuplicate();
266+
Object.keys(o).forEach(function(key) {
267+
var col = self.table.get(key);
268+
if(col && !col.autoGenerated)
269+
var val = o[key];
270+
onDuplicate.add(col.value(ParameterNode.getNodeOrParameterNode(val)));
271+
});
272+
273+
return self.add(onDuplicate);
274+
},
275+
276+
261277
forUpdate: function() {
262278
assert(typeof this._select !== 'undefined', 'FOR UPDATE can be used only in a select statement');
263279
this.add(new ForUpdate());

test/dialects/insert-tests.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,29 @@ Harness.test({
481481
params: [new Buffer('whoah'), new Buffer('hey')]
482482
});
483483

484+
Harness.test({
485+
query: post.insert({
486+
content: 'test',
487+
userId: 2
488+
}).onDuplicate({
489+
content: 'testupdate',
490+
}),
491+
pg: {
492+
throws: true
493+
},
494+
sqlite: {
495+
throws: true
496+
},
497+
mysql: {
498+
text : 'INSERT INTO `post` (`content`, `userId`) VALUES (?, ?) ON DUPLICATE KEY UPDATE `post`.`content` = ?',
499+
string: 'INSERT INTO `post` (`content`, `userId`) VALUES (\'test\', 2) ON DUPLICATE KEY UPDATE `post`.`content` = \'testupdate\''
500+
},
501+
mssql: {
502+
throws: true
503+
},
504+
params: ['test', 2, 'testupdate']
505+
});
506+
484507
Harness.test({
485508
query: post.insert([]),
486509

0 commit comments

Comments
 (0)