mongo-sql
Version:
A mongo-like interface for sql generation, postgres-style
974 lines (847 loc) • 23.4 kB
JavaScript
var assert = require('assert');
var builder = require('../');
describe('Built-In Query Types', function(){
describe('Type: select', function(){
it ('should build a query selecting on users with a schema', function(){
var query = builder.sql({
type: 'select'
, table: 'private.users'
});
assert.equal(
query.toString()
, 'select "private"."users".* from "private"."users"'
);
});
it ('should build a query selecting on users', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
});
assert.equal(
query.toString()
, 'select "users".* from "users"'
);
});
it ('should specify columns', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, columns: ['id', 'name']
});
assert.equal(
query.toString()
, 'select "users"."id", "users"."name" from "users"'
);
});
it ('should specify columns with schema', function(){
var query = builder.sql({
type: 'select'
, table: 'private.users'
, columns: ['private.users.id', 'private.users.name']
});
assert.equal(
query.toString()
, 'select "private"."users"."id", "private"."users"."name" from "private"."users"'
);
});
it ('should specify columns with schema even for simple column names', function(){
var query = builder.sql({
type: 'select'
, table: 'private.users'
, columns: ['id', 'name']
});
assert.equal(
query.toString()
, 'select "private"."users"."id", "private"."users"."name" from "private"."users"'
);
});
it ('should specify columns that are objects', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, columns: [
{ name: 'id', alias: 'user_id' }
, 'name'
, { name: 'test', table: 'things' }
]
});
assert.equal(
query.toString()
, 'select "users"."id" as "user_id", "users"."name", "things"."test" from "users"'
);
});
it ('should specify columns and use functions', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, columns: ['max(version) as max']
});
assert.equal(
query.toString()
, 'select max(version) as max from "users"'
);
});
it ('should specify columns and use a sub-query', function(){
var query = builder.sql({
type: 'select'
, columns: [
{
type: 'select'
, table: 'consumers'
, columns: ['id']
, as: 'u'
}
]
});
assert.equal(
query.toString()
, 'select (select "consumers"."id" from "consumers") as "u"'
);
});
it ('should specify multiple tables', function(){
var query = builder.sql({
type: 'select'
, table: ['users', 'groups']
});
assert.equal(
query.toString()
, 'select "users".* from "users", "groups"'
);
});
it ('should specify multiple tables and columns ' +
'from both tables assuming that if a table ' +
'name is not specified in the column definition, ' +
'then the first table is default', function(){
var query = builder.sql({
type: 'select'
, table: ['users', 'groups']
, columns: ['id', 'name', 'groups.name']
});
assert.equal(
query.toString()
, 'select "users"."id", "users"."name", "groups"."name" from "users", "groups"'
);
});
it ('should specify multiple tables and columns ' +
'from both tables assuming that if a table ' +
'name is not specified in the column definition, ' +
'then the first table is default, using an object' +
'as the column definition', function(){
var query = builder.sql({
type: 'select'
, table: ['users', 'groups']
, columns: {
'id': 'id'
, 'name': 'name'
, 'groups.name': 'group'
}
});
assert.equal(
query.toString()
, 'select "users"."id" as "id", "users"."name" as "name", "groups"."name" as "group" from "users", "groups"'
);
});
it ('group by string', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, groupBy: 'id'
});
assert.equal(
query.toString()
, 'select "users".* from "users" group by "users"."id"'
);
});
it ('group by array', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, groupBy: ['id', 'name']
});
assert.equal(
query.toString()
, 'select "users".* from "users" group by "users"."id", "users"."name"'
);
});
it ('group by expression', function(){
var expression = 'extract(hour from created_at)'
var query = builder.sql({
type: 'select'
, table: 'users'
, groupBy: [expression]
});
assert.equal(
query.toString()
, 'select "users".* from "users" group by extract(hour from created_at)'
);
});
it ('order by string', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, order: 'id desc'
});
assert.equal(
query.toString()
, 'select "users".* from "users" order by id desc'
);
});
it ('should put order before limit', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, order: 'id desc'
, limit: 5
});
assert.equal(
query.toString()
, 'select "users".* from "users" order by id desc limit $1'
);
});
it ('order by array', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, order: ['id desc', 'name asc']
});
assert.equal(
query.toString()
, 'select "users".* from "users" order by id desc, name asc'
);
});
it ('order by object', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, order: { id: 'desc' }
});
assert.equal(
query.toString()
, 'select "users".* from "users" order by "users"."id" desc'
);
});
it ('order by object, multiple', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, order: { id: 'desc', name: 'asc' }
});
assert.equal(
query.toString()
, 'select "users".* from "users" order by "users"."id" desc, "users"."name" asc'
);
});
it ('order by object, multiple tables', function(){
var query = builder.sql({
type: 'select'
, table: ['users', 'groups']
, order: { id: 'desc', 'groups.id': 'asc' }
});
assert.equal(
query.toString()
, 'select "users".* from "users", "groups" order by "users"."id" desc, "groups"."id" asc'
);
});
it ('limit', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, limit: 10
});
assert.equal(
query.toString()
, 'select "users".* from "users" limit $1'
);
assert.deepEqual(
query.values
, [10]
);
});
it ('limit all', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, limit: 'all'
});
assert.equal(
query.toString()
, 'select "users".* from "users" limit all'
);
});
it ('invalid limit throws error', function(){
assert.throws(function() {
builder.sql({
type: 'select'
, table: 'users'
, limit: 'qwerty'
});
}, Error );
});
it ('offset', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, offset: 10
});
assert.equal(
query.toString()
, 'select "users".* from "users" offset $1'
);
assert.deepEqual(
query.values
, [10]
);
});
it ('should support with', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, with: {
otherUsers: {
type: 'select'
, table: 'users'
, where: {
columnA: 'other'
}
}
}
, where: {
id: {
$nin: {
type: 'select'
, table: 'otherUsers'
, columns: ['id']
}
}
}
});
assert.equal(
query.toString()
, [
'with "otherUsers" as ('
, 'select "users".* from "users" '
, 'where "users"."columnA" = $1'
, ') '
, 'select "users".* from "users" '
, 'where "users"."id" not in ('
, 'select "otherUsers"."id" from "otherUsers"'
, ')'
].join('')
);
assert.deepEqual(
query.values
, ['other']
);
});
it ('should support multiple withs', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, with: {
otherUsers: {
type: 'select'
, table: 'users'
, where: {
columnA: 'other'
}
}
, otherUsers2: {
type: 'select'
, table: 'users'
, where: {
columnA: 'other2'
}
}
}
, where: {
id: {
$nin: {
type: 'select'
, table: 'otherUsers'
, columns: ['id']
}
}
}
});
assert.equal(
query.toString()
, [
'with "otherUsers" as ('
, 'select "users".* from "users" '
, 'where "users"."columnA" = $1'
, '), '
, '"otherUsers2" as ('
, 'select "users".* from "users" '
, 'where "users"."columnA" = $2'
, ') '
, 'select "users".* from "users" '
, 'where "users"."id" not in ('
, 'select "otherUsers"."id" from "otherUsers"'
, ')'
].join('')
);
assert.deepEqual(
query.values
, ['other', 'other2']
);
});
it ('should support multiple withs in array syntax', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, with: [
{
type: 'select'
, table: 'users'
, name: 'otherUsers'
, where: {
columnA: 'other'
}
}
, {
type: 'select'
, table: 'users'
, name: 'otherUsers2'
, where: {
columnA: 'other2'
}
}
, {
type: 'select'
, table: 'users'
, name: 'otherUsers3'
, where: {
columnA: 'other3'
}
}
]
, where: {
id: {
$nin: {
type: 'select'
, table: 'otherUsers'
, columns: ['id']
}
}
}
});
assert.equal(
query.toString()
, [
'with "otherUsers" as ('
, 'select "users".* from "users" '
, 'where "users"."columnA" = $1'
, '), '
, '"otherUsers2" as ('
, 'select "users".* from "users" '
, 'where "users"."columnA" = $2'
, '), '
, '"otherUsers3" as ('
, 'select "users".* from "users" '
, 'where "users"."columnA" = $3'
, ') '
, 'select "users".* from "users" '
, 'where "users"."id" not in ('
, 'select "otherUsers"."id" from "otherUsers"'
, ')'
].join('')
);
assert.deepEqual(
query.values
, ['other', 'other2', 'other3']
);
});
it ('should select distinct', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, distinct: true
});
assert.equal(
query.toString()
, 'select distinct "users".* from "users"'
);
});
it ('should not select distinct', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, distinct: false
});
assert.equal(
query.toString()
, 'select "users".* from "users"'
);
});
it ('should select distinct on single', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, distinct: ['id']
, order: ['id asc']
});
assert.equal(
query.toString()
, 'select distinct on ("id") "users".* from "users" order by id asc'
);
});
it ('should select distinct on multilpe', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, distinct: ['id', 'name']
, order: ['id asc', 'name asc']
});
assert.equal(
query.toString()
, 'select distinct on ("id", "name") "users".* from "users" order by id asc, name asc'
);
});
it ('should not select distinct on', function(){
var query = builder.sql({
type: 'select'
, table: 'users'
, distinct: []
, order: ['id asc']
});
assert.equal(
query.toString()
, 'select "users".* from "users" order by id asc'
);
});
it ('allow sub-queries on table', function(){
var query = builder.sql({
type: 'select'
, table: {
type: 'select'
, table: 'users'
}
, alias: 'u'
});
assert.equal(
query.toString()
, 'select "u".* from (select "users".* from "users") "u"'
);
});
it ('sub query and alias works correct', function(){
var query = builder.sql({
type: 'select'
, table: {
type: 'select'
, table: 'users'
}
, alias: 'u'
});
assert.equal(
query.toString()
, 'select "u".* from (select "users".* from "users") "u"'
);
});
it ('allow arbitrary sub-queries on table', function(){
var query = builder.sql({
type: 'select'
, table: {
type: 'select'
, table: {
type: 'select'
, table: {
type: 'select'
, table: 'users'
, alias: 'uuuu'
}
, alias: 'uuu'
}
, alias: 'uu'
},
alias: 'u'
});
assert.equal(
query.toString()
, [
'select "u".* from '
, '(select "uu".* from '
, '(select "uuu".* from '
, '(select "uuuu".* from "users" "uuuu") "uuu") "uu") "u"'
].join('')
);
});
it ('select expression', function(){
var query = builder.sql({
type: 'select'
, expression: {
type: 'unnest'
, expression: 'users.jobs'
}
});
assert.equal(
query.toString()
, 'select unnest( users.jobs )'
);
});
it ('select expression with parameters', function(){
var query = builder.sql({
type: 'select'
, table: 'tbl'
, columns: [
{ expression: { expression: '$1, $2', values: [ 3, 4 ] } }
]
, where: { col: 'bob' }
});
assert.equal(
query.toString()
, 'select $1, $2 from "tbl" where "tbl"."col" = $3'
);
assert.deepEqual(
query.values
, [ 3, 4, 'bob' ]
);
});
// TODO: make this test pass
// it ('should allow an empty over clause with string', function() {
// var query = builder.sql({
// type: 'select'
// , table: 'foo'
// , columns: ['bar', {type: 'function', function: 'avg', expression: 'baz'}]
// , over: ''
// });
// assert.equal(
// query.toString()
// , 'select "foo"."bar", avg( baz ) over () from "foo"'
// );
// });
it ('should allow an over clause with arbitrary string', function() {
var query = builder.sql({
type: 'select'
, table: 'foo'
, columns: ['bar', {type: 'function', function: 'avg', expression: 'baz'}]
, over: 'completely arbitrary'
});
assert.equal(
query.toString()
, 'select "foo"."bar", avg( baz ) over (completely arbitrary) from "foo"'
);
});
it ('should allow an empty over clause with object', function() {
var query = builder.sql({
type: 'select'
, table: 'foo'
, columns: ['bar', {type: 'function', function: 'avg', expression: 'baz'}]
, over: {}
});
assert.equal(
query.toString()
, 'select "foo"."bar", avg( baz ) over () from "foo"'
);
});
it ('should allow over clause with partition', function() {
var query = builder.sql({
type: 'select'
, table: 'foo'
, columns: ['bar', {type: 'function', function: 'avg', expression: 'baz'}]
, over: {partition: 'bar'}
});
assert.equal(
query.toString()
, 'select "foo"."bar", avg( baz ) over (partition by "foo"."bar") from "foo"'
);
});
it ('should allow over clause with order', function() {
var query = builder.sql({
type: 'select'
, table: 'foo'
, columns: ['bar', {type: 'function', function: 'avg', expression: 'baz'}]
, over: {order: 'bar'}
});
assert.equal(
query.toString()
, 'select "foo"."bar", avg( baz ) over (order by bar) from "foo"'
);
});
it ('should allow over clause with partition and order', function() {
var query = builder.sql({
type: 'select'
, table: 'foo'
, columns: ['bar', {type: 'function', function: 'avg', expression: 'baz'}]
, over: {
partition: 'bar'
, order: 'bar asc'
}
});
assert.equal(
query.toString()
, 'select "foo"."bar", avg( baz ) over (partition by "foo"."bar" order by bar asc) from "foo"'
);
});
it ('should allow over clause with array partition', function() {
var query = builder.sql({
type: 'select'
, table: 'foo'
, columns: ['bar', {type: 'function', function: 'avg', expression: 'baz'}]
, over: {
partition: ['bar', 'another']
}
});
assert.equal(
query.toString()
, 'select "foo"."bar", avg( baz ) over (partition by "foo"."bar", "foo"."another") from "foo"'
);
});
it ('should declare a window as an existing window', function() {
var query = builder.sql({
type: 'select'
, table: 'foo'
, window: {
name: 'f'
, as: {
existing: 'b'
}
}
});
assert.equal(
query.toString()
, 'select "foo".* from "foo" window "f" as ( "b" )'
);
});
it ('should declare a window as a partition by expression', function() {
var query = builder.sql({
type: 'select'
, table: 'foo'
, window: {
name: 'f'
, as: {
partition: 'b'
}
}
});
assert.equal(
query.toString()
, 'select "foo".* from "foo" window "f" as ( partition by "foo"."b" )'
);
});
it ('should declare a window as a partition by expression and order', function() {
var query = builder.sql({
type: 'select'
, table: 'foo'
, window: {
name: 'f'
, as: {
partition: 'b'
, order: { id: 'desc' }
}
}
});
assert.equal(
query.toString()
, 'select "foo".* from "foo" window "f" as ( partition by "foo"."b" order by "foo"."id" desc )'
);
});
it ('should let parenthesis in expressions be explicit', function() {
var query = builder.sql({
type: 'select'
, table: 'foo'
, columns: [
{ expression: 'bar' }
, {
expression: {
expression: 'bar - baz'
, parenthesis: true
}
, alias: 'blah'
}
]
});
assert.equal(
query.toString()
, 'select bar, ( bar - baz ) as "blah" from "foo"'
);
});
});
describe('Type: select for', function() {
it ('should select for update', function() {
var query = builder.sql({
type: 'select'
, table: 'foo'
, for: { type: 'update' }
});
assert.equal(
query.toString()
, 'select "foo".* from "foo" for update'
);
});
it ('should select for update on table', function() {
var query = builder.sql({
type: 'select'
, table: 'foo'
, for: { type: 'update', table: 'bar' }
});
assert.equal(
query.toString()
, 'select "foo".* from "foo" for update of "bar"'
);
});
it ('should select for share on table', function() {
var query = builder.sql({
type: 'select'
, table: 'foo'
, for: { type: 'share', table: 'bar' }
});
assert.equal(
query.toString()
, 'select "foo".* from "foo" for share of "bar"'
);
});
it ('should select for no key update on table', function() {
var query = builder.sql({
type: 'select'
, table: 'foo'
, for: { type: 'no key update', table: 'bar' }
});
assert.equal(
query.toString()
, 'select "foo".* from "foo" for no key update of "bar"'
);
});
it ('should select for key share on table', function() {
var query = builder.sql({
type: 'select'
, table: 'foo'
, for: { type: 'key share', table: 'bar' }
});
assert.equal(
query.toString()
, 'select "foo".* from "foo" for key share of "bar"'
);
});
it ('should select for update on multiple tables', function() {
var query = builder.sql({
type: 'select'
, table: 'foo'
, for: { type: 'update', table: ['bar', '"wiz"', 'wog'] }
});
assert.equal(
query.toString()
, 'select "foo".* from "foo" for update of "bar", "wiz", "wog"'
);
});
it ('should select for update nowait', function() {
var query = builder.sql({
type: 'select'
, table: 'foo'
, for: { type: 'update', noWait: true }
});
assert.equal(
query.toString()
, 'select "foo".* from "foo" for update nowait'
);
});
it ('should throw "for" helper requires type', function() {
assert.throws(function() {
var query = builder.sql({
type: 'select'
, table: 'foo'
, for: { noWait: true }
});
});
});
});
});