Skip to content

Commit c62c500

Browse files
iteufelojourmel
authored andcommitted
Added returning support for mssql.
1 parent 20600ff commit c62c500

File tree

4 files changed

+287
-87
lines changed

4 files changed

+287
-87
lines changed

lib/dialects/mssql/blocks.js

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
'use strict';
2+
3+
var _ = require('underscore');
4+
5+
module.exports = function(dialect) {
6+
dialect.blocks.set('limit', function(params) {
7+
return (!isNaN(params.offset)) ? '' : 'top(' + dialect.builder._pushValue(params.limit) + ')';
8+
});
9+
10+
dialect.blocks.set('offset', function(params) {
11+
var pre = (!params.sort) ? 'order by 1 ' : '';
12+
if (params.limit) {
13+
var str = pre + 'offset ' + dialect.builder._pushValue(params.offset);
14+
str += ' rows fetch next ' + dialect.builder._pushValue(params.limit) + ' rows only';
15+
return str;
16+
}else {
17+
return pre + 'OFFSET ' + dialect.builder._pushValue(params.offset) + ' rows';
18+
}
19+
});
20+
21+
dialect.blocks.set('returning', function(params) {
22+
var result = dialect.buildBlock('fields', {fields: params.returning});
23+
24+
if (result) result = 'output ' + result;
25+
26+
return result;
27+
});
28+
29+
dialect.blocks.set('insert:values', function(params) {
30+
var values = params.values;
31+
32+
if (!_.isArray(values)) values = [values];
33+
34+
var fields = params.fields || _(values)
35+
.chain()
36+
.map(function(row) {
37+
return _(row).keys();
38+
})
39+
.flatten()
40+
.uniq()
41+
.value();
42+
43+
return dialect.buildTemplate('insertValues', {
44+
fields: fields,
45+
returning: params.returning || undefined,
46+
values: _(values).map(function(row) {
47+
return _(fields).map(function(field) {
48+
return dialect.buildBlock('value', {value: row[field]});
49+
});
50+
})
51+
});
52+
});
53+
54+
dialect.blocks.add('insertValues:values', function(params) {
55+
return _(params.values).map(function(row) {
56+
return '(' + row.join(', ') + ')';
57+
}).join(', ');
58+
});
59+
};

lib/dialects/mssql/index.js

+37-87
Original file line numberDiff line numberDiff line change
@@ -3,97 +3,47 @@
33
var BaseDialect = require('../base');
44
var _ = require('underscore');
55
var util = require('util');
6+
var templatesInit = require('./templates');
7+
var blocksInit = require('./blocks');
68
var templateChecks = require('../../utils/templateChecks');
79

810
var Dialect = module.exports = function(builder) {
9-
builder.options.valuesPrefix = '@';
10-
11-
builder._pushValue = function(value) {
12-
if (_.isUndefined(value) || _.isNull(value)) {
13-
return 'null';
14-
} else if (_.isBoolean(value)) {
15-
return String(Number(value));
16-
} else if (_.isNumber(value)) {
17-
return String(value);
18-
} else if (_.isString(value) || _.isDate(value)) {
19-
if (this.options.separatedValues) {
20-
var placeholder = this._getPlaceholder();
2111

22-
if (this.options.namedValues) {
23-
this._values[placeholder] = value;
24-
} else {
25-
this._values.push(value);
26-
}
27-
28-
return this._wrapPlaceholder(placeholder);
29-
} else {
30-
if (_.isDate(value)) value = value.toISOString();
31-
32-
return '\'' + value + '\'';
33-
}
34-
} else {
35-
throw new Error('Wrong value type "' + (typeof value) + '"');
36-
}
37-
};
38-
39-
BaseDialect.call(this, builder);
40-
41-
this.blocks.set('limit', function(params) {
42-
return (!isNaN(params.offset)) ? '' : 'TOP(' + builder._pushValue(params.limit) + ')';
43-
});
44-
45-
this.blocks.set('offset', function(params) {
46-
var pre = (!params.sort) ? 'ORDER BY 1 ' : '';
47-
if (params.limit) {
48-
var str = pre + 'OFFSET ' + builder._pushValue(params.offset);
49-
str += ' ROWS FETCH NEXT ' + builder._pushValue(params.limit) + ' ROWS ONLY';
50-
return str;
51-
}else {
52-
return pre + 'OFFSET ' + builder._pushValue(params.offset) + ' ROWS';
53-
}
54-
});
55-
56-
this.templates.set('select', {
57-
pattern: '{with} {withRecursive} select {limit} {distinct} {fields} ' +
58-
'from {from} {table} {query} {select} {expression} {alias} ' +
59-
'{join} {condition} {group} {having} {sort} {offset}',
60-
defaults: {
61-
fields: {}
62-
},
63-
validate: function(type, params) {
64-
templateChecks.onlyOneOfProps(type, params, ['with', 'withRecursive']);
65-
templateChecks.propType(type, params, 'with', 'object');
66-
templateChecks.propType(type, params, 'withRecursive', 'object');
67-
68-
templateChecks.propType(type, params, 'distinct', 'boolean');
69-
70-
templateChecks.propType(type, params, 'fields', ['array', 'object']);
71-
72-
templateChecks.propType(type, params, 'from', ['string', 'array', 'object']);
73-
74-
templateChecks.atLeastOneOfProps(type, params, ['table', 'query', 'select', 'expression']);
75-
templateChecks.onlyOneOfProps(type, params, ['table', 'query', 'select', 'expression']);
76-
77-
templateChecks.propType(type, params, 'table', 'string');
78-
templateChecks.propType(type, params, 'query', 'object');
79-
templateChecks.propType(type, params, 'select', 'object');
80-
templateChecks.propType(type, params, 'expression', ['string', 'object']);
81-
82-
templateChecks.propType(type, params, 'alias', ['string', 'object']);
83-
84-
templateChecks.propType(type, params, 'join', ['array', 'object']);
85-
86-
templateChecks.propType(type, params, 'condition', ['array', 'object']);
87-
templateChecks.propType(type, params, 'having', ['array', 'object']);
88-
89-
templateChecks.propType(type, params, 'group', ['string', 'array']);
90-
91-
templateChecks.propType(type, params, 'sort', ['string', 'array', 'object']);
92-
93-
templateChecks.propType(type, params, 'offset', ['number', 'string']);
94-
templateChecks.propType(type, params, 'limit', ['number', 'string']);
95-
}
96-
});
12+
builder._pushValue = function(value) {
13+
if (_.isUndefined(value) || _.isNull(value)) {
14+
return 'null';
15+
} else if (_.isBoolean(value)) {
16+
return String(Number(value));
17+
} else if (_.isNumber(value)) {
18+
return String(value);
19+
} else if (_.isString(value) || _.isDate(value)) {
20+
if (this.options.separatedValues) {
21+
var placeholder = this._getPlaceholder();
22+
23+
if (this.options.namedValues) {
24+
this._values[placeholder] = value;
25+
} else {
26+
this._values.push(value);
27+
}
28+
29+
return this._wrapPlaceholder(placeholder);
30+
} else {
31+
if (_.isDate(value)) value = value.toISOString();
32+
33+
return '\'' + value + '\'';
34+
}
35+
} else {
36+
throw new Error('Wrong value type "' + (typeof value) + '"');
37+
}
38+
};
39+
40+
BaseDialect.call(this, builder);
41+
42+
// init templates
43+
templatesInit(this);
44+
45+
// init blocks
46+
blocksInit(this);
9747

9848
};
9949

lib/dialects/mssql/templates.js

+131
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
'use strict';
2+
3+
var _ = require('underscore');
4+
var templateChecks = require('../../utils/templateChecks');
5+
var orRegExp = /^(rollback|abort|replace|fail|ignore)$/i;
6+
7+
module.exports = function(dialect) {
8+
dialect.templates.set('select', {
9+
pattern: '{with} {withRecursive} select {limit} {distinct} {fields} ' +
10+
'from {from} {table} {query} {select} {expression} {alias} ' +
11+
'{join} {condition} {group} {having} {sort} {offset}',
12+
defaults: {
13+
fields: {}
14+
},
15+
validate: function(type, params) {
16+
templateChecks.onlyOneOfProps(type, params, ['with', 'withRecursive']);
17+
templateChecks.propType(type, params, 'with', 'object');
18+
templateChecks.propType(type, params, 'withRecursive', 'object');
19+
20+
templateChecks.propType(type, params, 'distinct', 'boolean');
21+
22+
templateChecks.propType(type, params, 'fields', ['array', 'object']);
23+
24+
templateChecks.propType(type, params, 'from', ['string', 'array', 'object']);
25+
26+
templateChecks.atLeastOneOfProps(type, params, ['table', 'query', 'select', 'expression']);
27+
templateChecks.onlyOneOfProps(type, params, ['table', 'query', 'select', 'expression']);
28+
29+
templateChecks.propType(type, params, 'table', 'string');
30+
templateChecks.propType(type, params, 'query', 'object');
31+
templateChecks.propType(type, params, 'select', 'object');
32+
templateChecks.propType(type, params, 'expression', ['string', 'object']);
33+
34+
templateChecks.propType(type, params, 'alias', ['string', 'object']);
35+
36+
templateChecks.propType(type, params, 'join', ['array', 'object']);
37+
38+
templateChecks.propType(type, params, 'condition', ['array', 'object']);
39+
templateChecks.propType(type, params, 'having', ['array', 'object']);
40+
41+
templateChecks.propType(type, params, 'group', ['string', 'array']);
42+
43+
templateChecks.propType(type, params, 'sort', ['string', 'array', 'object']);
44+
45+
templateChecks.propType(type, params, 'offset', ['number', 'string']);
46+
templateChecks.propType(type, params, 'limit', ['number', 'string']);
47+
}
48+
});
49+
50+
51+
dialect.templates.add('insert', {
52+
pattern: '{with} {withRecursive} insert {or} into {table} {values} ' +
53+
'{condition}',
54+
validate: function(type, params) {
55+
templateChecks.onlyOneOfProps(type, params, ['with', 'withRecursive']);
56+
templateChecks.propType(type, params, 'with', 'object');
57+
templateChecks.propType(type, params, 'withRecursive', 'object');
58+
59+
templateChecks.propType(type, params, 'or', 'string');
60+
templateChecks.propMatch(type, params, 'or', orRegExp);
61+
62+
templateChecks.requiredProp(type, params, 'table');
63+
templateChecks.propType(type, params, 'table', 'string');
64+
65+
templateChecks.requiredProp(type, params, 'values');
66+
templateChecks.propType(type, params, 'values', ['array', 'object']);
67+
68+
templateChecks.propType(type, params, 'condition', ['array', 'object']);
69+
70+
}
71+
});
72+
73+
dialect.templates.add('insertValues', {
74+
pattern: '({fields}) {returning} values {values}',
75+
validate: function(type, params) {
76+
templateChecks.requiredProp('values', params, 'fields');
77+
templateChecks.propType('values', params, 'fields', 'array');
78+
templateChecks.minPropLength('values', params, 'fields', 1);
79+
80+
templateChecks.propType(type, params, 'returning', ['array', 'object']);
81+
82+
templateChecks.requiredProp('values', params, 'values');
83+
templateChecks.propType('values', params, 'values', 'array');
84+
templateChecks.minPropLength('values', params, 'values', 1);
85+
}
86+
});
87+
88+
dialect.templates.add('update', {
89+
pattern: '{with} {withRecursive} update {or} {table} {alias} {modifier} {returning} {condition} ',
90+
validate: function(type, params) {
91+
templateChecks.onlyOneOfProps(type, params, ['with', 'withRecursive']);
92+
templateChecks.propType(type, params, 'with', 'object');
93+
templateChecks.propType(type, params, 'withRecursive', 'object');
94+
95+
templateChecks.propType(type, params, 'or', 'string');
96+
templateChecks.propMatch(type, params, 'or', orRegExp);
97+
98+
templateChecks.requiredProp(type, params, 'table');
99+
templateChecks.propType(type, params, 'table', 'string');
100+
101+
templateChecks.propType(type, params, 'returning', ['array', 'object']);
102+
103+
templateChecks.propType(type, params, 'alias', 'string');
104+
105+
templateChecks.requiredProp(type, params, 'modifier');
106+
templateChecks.propType(type, params, 'modifier', 'object');
107+
108+
templateChecks.propType(type, params, 'condition', ['array', 'object']);
109+
110+
}
111+
});
112+
113+
dialect.templates.add('remove', {
114+
pattern: '{with} {withRecursive} delete from {table} {returning} {alias} {condition} ',
115+
validate: function(type, params) {
116+
templateChecks.onlyOneOfProps(type, params, ['with', 'withRecursive']);
117+
templateChecks.propType(type, params, 'with', 'object');
118+
templateChecks.propType(type, params, 'withRecursive', 'object');
119+
120+
templateChecks.requiredProp(type, params, 'table');
121+
templateChecks.propType(type, params, 'table', 'string');
122+
123+
templateChecks.propType(type, params, 'returning', ['array', 'object']);
124+
125+
templateChecks.propType(type, params, 'alias', 'string');
126+
127+
templateChecks.propType(type, params, 'condition', ['array', 'object']);
128+
129+
}
130+
});
131+
};

tests/6_dialects/1_mssql.js

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
'use strict';
2+
3+
var jsonSql = require('../../lib')({
4+
dialect: 'mssql',
5+
namedValues: false
6+
});
7+
var expect = require('chai').expect;
8+
9+
describe('MSSQL dialect', function() {
10+
describe('limit', function() {
11+
it('should be ok with `limit` property', function() {
12+
var result = jsonSql.build({
13+
table: 'test',
14+
fields: ['user'],
15+
limit: 1,
16+
condition: {
17+
'name': {$eq: 'test'}
18+
}
19+
});
20+
expect(result.query).to.be.equal('select top(1) "user" from "test" where "name" = $1;');
21+
});
22+
23+
it('should be ok with `limit` and `offset` properties', function() {
24+
var result = jsonSql.build({
25+
table: 'test',
26+
fields: ['user'],
27+
limit: 4,
28+
offset: 2,
29+
condition: {
30+
'name': {$eq: 'test'}
31+
}
32+
});
33+
expect(result.query).to.be.equal('select "user" from "test" where "name" = $1 order by 1 offset 2 rows fetch next 4 rows only;');
34+
});
35+
});
36+
describe('returning', function() {
37+
it('should be ok with `remove` type', function() {
38+
var result = jsonSql.build({
39+
type: 'remove',
40+
table: 'test',
41+
returning: ['DELETED.*'],
42+
condition: {
43+
Description: {$eq: 'test'}
44+
}
45+
});
46+
expect(result.query).to.be.equal('delete from "test" output "DELETED".* where "Description" = $1;');
47+
});
48+
it('should be ok with `insert` type', function() {
49+
var result = jsonSql.build({
50+
type: 'insert',
51+
table: 'test',
52+
returning: ['INSERTED.*'],
53+
values: {
54+
Description: 'test',
55+
}
56+
});
57+
expect(result.query).to.be.equal('insert into "test" ("Description") output "INSERTED".* values ($1);');
58+
});
59+
});
60+
});

0 commit comments

Comments
 (0)