Browse Source

Merge pull request #49 from brianc/object-literals

Object literals
auto-join
Brian C 12 years ago
parent
commit
98f32243c1
  1. 5
      lib/node/binary.js
  2. 1
      lib/node/column.js
  3. 2
      lib/node/default.js
  4. 22
      lib/node/index.js
  5. 1
      lib/node/insert.js
  6. 4
      lib/node/join.js
  7. 4
      lib/node/orderByColumn.js
  8. 4
      lib/node/parameter.js
  9. 70
      lib/node/query.js
  10. 4
      lib/node/table.js
  11. 1
      lib/node/text.js
  12. 4
      lib/node/unary.js
  13. 40
      lib/node/where.js
  14. 8
      lib/table.js
  15. 4
      package.json
  16. 1
      test/clause-definition.js
  17. 25
      test/dialects/delete-tests.js
  18. 9
      test/dialects/group-by-tests.js
  19. 38
      test/dialects/insert-tests.js
  20. 1
      test/dialects/join-tests.js
  21. 3
      test/dialects/namespace-tests.js
  22. 7
      test/dialects/order-tests.js
  23. 9
      test/dialects/shortcut-tests.js
  24. 8
      test/dialects/table-tests.js

5
lib/node/binary.js

@ -1,8 +1,9 @@
'use strict'; 'use strict';
var Node = require(__dirname);
var BinaryNode = module.exports = require(__dirname).define({ var BinaryNode = module.exports = Node.define({
type: 'BINARY', type: 'BINARY',
constructor: function(config) { constructor: function(config) {
Node.call(this);
this.left = config.left; this.left = config.left;
this.operator = config.operator; this.operator = config.operator;
this.right = config.right; this.right = config.right;

1
lib/node/column.js

@ -5,6 +5,7 @@ var Node = require(__dirname);
module.exports = Node.define({ module.exports = Node.define({
type: 'COLUMN', type: 'COLUMN',
constructor: function(config) { constructor: function(config) {
Node.call(this);
this.name = config.name; this.name = config.name;
this.alias = config.alias; this.alias = config.alias;
this.star = config.star; this.star = config.star;

2
lib/node/default.js

@ -2,8 +2,6 @@
module.exports = require(__dirname).define({ module.exports = require(__dirname).define({
type: 'DEFAULT', type: 'DEFAULT',
constructor: function() {
},
value: function() { value: function() {
return; return;
} }

22
lib/node/index.js

@ -1,5 +1,6 @@
'use strict'; 'use strict';
var util = require('util');
var assert = require('assert'); var assert = require('assert');
var Node = function(type) { var Node = function(type) {
@ -16,18 +17,23 @@ Node.prototype.add = function(node) {
return this; return this;
}; };
Node.prototype.addAll = function(nodes) {
for(var i = 0, len = nodes.length; i < len; i++) {
this.add(nodes[i]);
}
return this;
}
Node.define = function(def) { Node.define = function(def) {
var c = function() { var c = function() {
Node.apply(this, arguments); Node.call(this);
if(def.constructor) {
def.constructor.apply(this, arguments);
}
}; };
var key; //allow custom sub-class constructor
for(key in Node.prototype) { if(def.constructor && def.constructor != {}.constructor) {
c.prototype[key] = Node.prototype[key]; c = def.constructor;
} }
for(key in def) { util.inherits(c, Node);
for(var key in def) {
c.prototype[key] = def[key]; c.prototype[key] = def[key];
} }
return c; return c;

1
lib/node/insert.js

@ -7,6 +7,7 @@ var DefaultNode = require('./default');
var Insert = Node.define({ var Insert = Node.define({
type: 'INSERT', type: 'INSERT',
constructor: function () { constructor: function () {
Node.call(this);
this.names = []; this.names = [];
this.columns = []; this.columns = [];
this.valueSets = []; this.valueSets = [];

4
lib/node/join.js

@ -1,8 +1,10 @@
'use strict'; 'use strict';
var JoinNode = module.exports = require(__dirname).define({ var Node = require(__dirname);
var JoinNode = module.exports = Node.define({
type: 'JOIN', type: 'JOIN',
constructor: function(subType, from, to) { constructor: function(subType, from, to) {
Node.call(this);
this.subType = subType; this.subType = subType;
this.from = from.toNode(); this.from = from.toNode();
this.to = to.toNode(); this.to = to.toNode();

4
lib/node/orderByColumn.js

@ -1,8 +1,10 @@
'use strict'; 'use strict';
var OrderByColumn = module.exports = require(__dirname).define({ var Node = require(__dirname);
var OrderByColumn = module.exports = Node.define({
type: 'ORDER BY COLUMN', type: 'ORDER BY COLUMN',
constructor: function(config) { constructor: function(config) {
Node.call(this);
this.column = config.column; this.column = config.column;
this.direction = config.direction; this.direction = config.direction;
} }

4
lib/node/parameter.js

@ -1,8 +1,10 @@
'use strict'; 'use strict';
module.exports = require(__dirname).define({ var Node = require(__dirname);
module.exports = Node.define({
type: 'PARAMETER', type: 'PARAMETER',
constructor: function(val) { constructor: function(val) {
Node.call(this);
this._val = val; this._val = val;
}, },
value: function() { value: function() {

70
lib/node/query.js

@ -1,4 +1,7 @@
'use strict'; 'use strict';
var util = require('util');
var sliced = require('sliced');
var Node = require(__dirname); var Node = require(__dirname);
var Select = require(__dirname + '/select'); var Select = require(__dirname + '/select');
@ -32,15 +35,16 @@ var Modifier = Node.define({
var Query = Node.define({ var Query = Node.define({
type: 'QUERY', type: 'QUERY',
constructor: function(table) { constructor: function(table) {
Node.call(this);
this.table = table; this.table = table;
}, },
select: function() { select: function() {
var select = new Select(); var select = new Select();
var args = Array.prototype.slice.call(arguments, 0); var args = sliced(arguments);
for(var i = 0; i < args.length; i++) { if(util.isArray(args[0])) {
//arg could be a column instead of a node args = args[0];
select.add(args[i]);
} }
select.addAll(args);
// if this is a subquery then add reference to this column // if this is a subquery then add reference to this column
if(this.type === 'SUBQUERY') { if(this.type === 'SUBQUERY') {
for(var j = 0; j < select.nodes.length; j++) { for(var j = 0; j < select.nodes.length; j++) {
@ -64,28 +68,12 @@ var Query = Node.define({
return this.add(from); return this.add(from);
}, },
where: function(node) { where: function(node) {
if (!node.toNode && typeof node == 'object')
node = this._whereObject(node);
//calling #where twice functions like calling #where & then #and //calling #where twice functions like calling #where & then #and
if(this.whereClause) return this.and(node); if(this.whereClause) return this.and(node);
this.whereClause = new Where(); this.whereClause = new Where(this.table);
this.whereClause.add( this.whereClause.expr(node) ); this.whereClause.add(node);
return this.add(this.whereClause); return this.add(this.whereClause);
}, },
_whereObject: function(node) {
var result;
for (var colName in node) {
var column = new Column({name: colName, table: this.table});
var query = column.equals(node[colName]);
if (!result)
result = query;
else
result.and(query);
}
return result;
},
or: function(node) { or: function(node) {
this.whereClause.or(node); this.whereClause.or(node);
return this; return this;
@ -95,25 +83,25 @@ var Query = Node.define({
return this; return this;
}, },
order: function() { order: function() {
var args = Array.prototype.slice.call(arguments, 0); var args = sliced(arguments);
var orderBy = new OrderBy(); if(util.isArray(args[0])) {
var nodes = args.forEach(function(arg) { var args = args[0]
orderBy.add(arg.toNode()); }
}); var orderBy = new OrderBy().addAll(args);
return this.add(orderBy); return this.add(orderBy);
}, },
group: function() { group: function() {
var args = Array.prototype.slice.call(arguments, 0); var args = sliced(arguments);
var groupBy = new GroupBy(); if(util.isArray(args[0])) {
var nodes = args.forEach(function(arg) { args = args[0];
groupBy.add(arg.toNode()); }
}); var groupBy = new GroupBy().addAll(args);
return this.add(groupBy); return this.add(groupBy);
}, },
insert: function(o) { insert: function(o) {
var self = this; var self = this;
var args = Array.prototype.slice.call(arguments, 0); var args = sliced(arguments);
//object literal //object literal
if(arguments.length == 1 && !o.toNode && !o.forEach) { if(arguments.length == 1 && !o.toNode && !o.forEach) {
args = Object.keys(o).map(function(key) { args = Object.keys(o).map(function(key) {
@ -146,16 +134,20 @@ var Query = Node.define({
}); });
return this.add(update); return this.add(update);
}, },
'delete': function() { 'delete': function(params) {
return this.add(new Delete()); var result = this.add(new Delete());
if(params) {
result = this.where(params);
}
return result;
}, },
returning: function() { returning: function() {
var returning = new Returning(); var returning = new Returning();
var args = Array.prototype.slice.call(arguments, 0); var args = sliced(arguments);
for(var i = 0; i < args.length; i++) { if(util.isArray(args[0])) {
//arg could be a column instead of a node args = args[0];
returning.add(args[i]);
} }
returning.addAll(args);
return this.add(returning); return this.add(returning);
}, },
create: function() { create: function() {

4
lib/node/table.js

@ -1,8 +1,10 @@
'use strict'; 'use strict';
module.exports = require(__dirname).define({ var Node = require(__dirname);
module.exports = Node.define({
type: 'TABLE', type: 'TABLE',
constructor: function(table) { constructor: function(table) {
Node.call(this);
this.table = table; this.table = table;
} }
}); });

1
lib/node/text.js

@ -5,6 +5,7 @@ var Node = require(__dirname);
module.exports = Node.define({ module.exports = Node.define({
type: 'TEXT', type: 'TEXT',
constructor: function(text) { constructor: function(text) {
Node.call(this);
this.text = text; this.text = text;
} }
}); });

4
lib/node/unary.js

@ -1,8 +1,10 @@
'use strict'; 'use strict';
module.exports = require(__dirname).define({ var Node = require(__dirname);
module.exports = Node.define({
type: 'UNARY', type: 'UNARY',
constructor: function(config) { constructor: function(config) {
Node.call(this);
this.left = config.left, this.left = config.left,
this.operator = config.operator; this.operator = config.operator;
} }

40
lib/node/where.js

@ -1,24 +1,54 @@
'use strict'; 'use strict';
var Node = require(__dirname);
var Column = require(__dirname + '/../column');
var BinaryNode = require(__dirname + '/binary'); var BinaryNode = require(__dirname + '/binary');
var TextNode = require(__dirname + '/text'); var TextNode = require(__dirname + '/text');
module.exports = require(__dirname).define({
var normalizeNode = function(table, node) {
var result = node;
if(typeof node == 'string') {
result = new TextNode('(' + node + ')');
}
else if (!node.toNode && typeof node == 'object'){
result = false;
for (var colName in node) {
var column = new Column({name: colName, table: table});
var query = column.equals(node[colName]);
if (!result)
result = query;
else
result.and(query);
}
}
return result;
};
module.exports = Node.define({
constructor: function(table) {
Node.call(this);
this.table = table;
},
type: 'WHERE', type: 'WHERE',
expr: function(other) { add: function(node) {
return typeof other === 'string' ? new TextNode('('+other+')') : other; var node = normalizeNode(this.table, node);
return Node.prototype.add.call(this, node);
}, },
or: function(other) { or: function(other) {
var right = normalizeNode(this.table, other);
return this.nodes.push(new BinaryNode({ return this.nodes.push(new BinaryNode({
left: this.nodes.pop(), left: this.nodes.pop(),
operator: 'OR', operator: 'OR',
right: this.expr(other) right: right
})); }));
}, },
and: function(other) { and: function(other) {
var right = normalizeNode(this.table, other);
return this.nodes.push(new BinaryNode({ return this.nodes.push(new BinaryNode({
left: this.nodes.pop(), left: this.nodes.pop(),
operator: 'AND', operator: 'AND',
right: this.expr(other) right: right
})); }));
} }
}); });

8
lib/table.js

@ -37,6 +37,14 @@ Table.prototype.addColumn = function(col) {
return this; return this;
}; };
Table.prototype.getColumn = function(colName) {
var col = this[colName];
if(!col) {
throw new Error('Table ' + this._name + ' does not have a column named ' + colName);
}
return this;
}
Table.prototype.getSchema = function() { Table.prototype.getSchema = function() {
return this._schema; return this._schema;
}; };

4
package.json

@ -15,7 +15,9 @@
"engines": { "engines": {
"node": "*" "node": "*"
}, },
"dependencies": {}, "dependencies": {
"sliced": "0.0.5"
},
"devDependencies": { "devDependencies": {
"test-dir" : "*", "test-dir" : "*",
"tap" : "*" "tap" : "*"

1
test/clause-definition.js

@ -10,6 +10,7 @@ var Bang = Node.define({
var Boom = Node.define({ var Boom = Node.define({
constructor: function(n) { constructor: function(n) {
Node.call(this);
this.name = n; this.name = n;
} }
}); });

25
test/dialects/delete-tests.js

@ -10,3 +10,28 @@ Harness.test({
mysql : 'DELETE FROM `post` WHERE (`post`.`content` = ?)', mysql : 'DELETE FROM `post` WHERE (`post`.`content` = ?)',
params: [''] params: ['']
}); });
Harness.test({
query : post.delete().where({content: ''}),
pg : 'DELETE FROM "post" WHERE ("post"."content" = $1)',
sqlite: 'DELETE FROM "post" WHERE ("post"."content" = $1)',
mysql : 'DELETE FROM `post` WHERE (`post`.`content` = ?)',
params: ['']
});
Harness.test({
query : post.delete({content: ''}),
pg : 'DELETE FROM "post" WHERE ("post"."content" = $1)',
sqlite: 'DELETE FROM "post" WHERE ("post"."content" = $1)',
mysql : 'DELETE FROM `post` WHERE (`post`.`content` = ?)',
params: ['']
});
Harness.test({
query : post.delete({content: ''}).or(post.content.isNull()),
pg : 'DELETE FROM "post" WHERE (("post"."content" = $1) OR ("post"."content" IS NULL))',
sqlite: 'DELETE FROM "post" WHERE (("post"."content" = $1) OR ("post"."content" IS NULL))',
mysql : 'DELETE FROM `post` WHERE ((`post`.`content` = ?) OR (`post`.`content` IS NULL))',
params: ['']
});

9
test/dialects/group-by-tests.js

@ -35,3 +35,12 @@ Harness.test({
mysql : 'SELECT GROUP_CONCAT(`post`.`content`) AS `post contents` FROM `post` GROUP BY `post`.`userId`', mysql : 'SELECT GROUP_CONCAT(`post`.`content`) AS `post contents` FROM `post` GROUP BY `post`.`userId`',
params: [] params: []
}); });
Harness.test({
query : post.select(post.content).group([post.userId, post.id]),
pg : 'SELECT "post"."content" FROM "post" GROUP BY "post"."userId", "post"."id"',
sqlite: 'SELECT "post"."content" FROM "post" GROUP BY "post"."userId", "post"."id"',
mysql : 'SELECT `post`.`content` FROM `post` GROUP BY `post`.`userId`, `post`.`id`',
params: []
});

38
test/dialects/insert-tests.js

@ -97,4 +97,42 @@ Harness.test({
params: [] params: []
}); });
Harness.test({
query : post.insert({}).returning('*'),
pg : 'INSERT INTO "post" DEFAULT VALUES RETURNING *',
sqlite: 'INSERT INTO "post" DEFAULT VALUES RETURNING *',
mysql : 'INSERT INTO `post` () VALUES () RETURNING *',
params: []
});
Harness.test({
query : post.insert({}).returning(post.star()),
pg : 'INSERT INTO "post" DEFAULT VALUES RETURNING *',
sqlite: 'INSERT INTO "post" DEFAULT VALUES RETURNING *',
mysql : 'INSERT INTO `post` () VALUES () RETURNING *',
params: []
});
Harness.test({
query : post.insert({}).returning(post.id),
pg : 'INSERT INTO "post" DEFAULT VALUES RETURNING "id"',
sqlite: 'INSERT INTO "post" DEFAULT VALUES RETURNING "id"',
mysql : 'INSERT INTO `post` () VALUES () RETURNING `id`',
params: []
});
Harness.test({
query : post.insert({}).returning(post.id, post.content),
pg : 'INSERT INTO "post" DEFAULT VALUES RETURNING "id", "content"',
sqlite: 'INSERT INTO "post" DEFAULT VALUES RETURNING "id", "content"',
mysql : 'INSERT INTO `post` () VALUES () RETURNING `id`, `content`',
params: []
});
Harness.test({
query : post.insert({}).returning([post.id, post.content]),
pg : 'INSERT INTO "post" DEFAULT VALUES RETURNING "id", "content"',
sqlite: 'INSERT INTO "post" DEFAULT VALUES RETURNING "id", "content"',
mysql : 'INSERT INTO `post` () VALUES () RETURNING `id`, `content`',
params: []
});

1
test/dialects/join-tests.js

@ -67,4 +67,3 @@ Harness.test({
sqlite: '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: '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 : '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 : 'SELECT `user`.`name`, `subposts`.`content` FROM `user` INNER JOIN (SELECT `post`.`content`, `post`.`userId` AS `subpostUserId` FROM `post`) subposts ON (`user`.`id` = `subposts`.`subpostUserId`)'
}); });

3
test/dialects/namespace-tests.js

@ -55,6 +55,3 @@ Harness.test({
sqlite: 'SELECT "comment"."text", "comment"."userId" FROM "comment"', sqlite: 'SELECT "comment"."text", "comment"."userId" FROM "comment"',
mysql : 'SELECT `comment`.`text`, `comment`.`userId` FROM `comment`' mysql : 'SELECT `comment`.`text`, `comment`.`userId` FROM `comment`'
}); });

7
test/dialects/order-tests.js

@ -24,3 +24,10 @@ Harness.test({
sqlite: 'SELECT "post"."content" FROM "post" ORDER BY "post"."content", "post"."userId" DESC', sqlite: 'SELECT "post"."content" FROM "post" ORDER BY "post"."content", "post"."userId" DESC',
mysql : 'SELECT `post`.`content` FROM `post` ORDER BY `post`.`content`, `post`.`userId` DESC' mysql : 'SELECT `post`.`content` FROM `post` ORDER BY `post`.`content`, `post`.`userId` DESC'
}); });
Harness.test({
query : post.select(post.content).order([post.content, post.userId.descending]),
pg : 'SELECT "post"."content" FROM "post" ORDER BY "post"."content", "post"."userId" DESC',
sqlite: 'SELECT "post"."content" FROM "post" ORDER BY "post"."content", "post"."userId" DESC',
mysql : 'SELECT `post`.`content` FROM `post` ORDER BY `post`.`content`, `post`.`userId` DESC'
});

9
test/dialects/shortcut-tests.js

@ -44,5 +44,10 @@ Harness.test({
params: [1] params: [1]
}); });
Harness.test({
query : post.where(post.content.isNull()).or({content: ''}).and({userId: 1}),
pg : 'SELECT "post".* FROM "post" WHERE ((("post"."content" IS NULL) OR ("post"."content" = $1)) AND ("post"."userId" = $2))',
sqlite: 'SELECT "post".* FROM "post" WHERE ((("post"."content" IS NULL) OR ("post"."content" = $1)) AND ("post"."userId" = $2))',
mysql : 'SELECT `post`.* FROM `post` WHERE (((`post`.`content` IS NULL) OR (`post`.`content` = ?)) AND (`post`.`userId` = ?))',
params: ['', 1]
});

8
test/dialects/table-tests.js

@ -78,6 +78,14 @@ Harness.test({
params: ['foo', 'bar', 1] params: ['foo', 'bar', 1]
}); });
Harness.test({
query : user.select(user.columns),
pg : 'SELECT "user"."id", "user"."name" FROM "user"',
sqlite: 'SELECT "user"."id", "user"."name" FROM "user"',
mysql : 'SELECT `user`.`id`, `user`.`name` FROM `user`',
params: []
});
Harness.test({ Harness.test({
query : user query : user

Loading…
Cancel
Save