-
Notifications
You must be signed in to change notification settings - Fork 79
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add MSSQL support #26
base: master
Are you sure you want to change the base?
Changes from all commits
2d7a382
ce084ef
c0a6042
62c1d5d
a138b2c
dd3467d
0262ad1
df972bc
6124cfc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
'use strict'; | ||
|
||
var _ = require('underscore'); | ||
|
||
module.exports = function(dialect) { | ||
dialect.blocks.set('limit', function(params) { | ||
return (!isNaN(params.offset)) ? '' : 'top(' + dialect.builder._pushValue(params.limit) + ')'; | ||
}); | ||
|
||
dialect.blocks.set('offset', function(params) { | ||
var pre = (!params.sort) ? 'order by 1 ' : ''; | ||
if (params.limit) { | ||
var str = pre + 'offset ' + dialect.builder._pushValue(params.offset); | ||
str += ' rows fetch next ' + dialect.builder._pushValue(params.limit) + ' rows only'; | ||
return str; | ||
}else { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. whitespace is missed here |
||
return pre + 'OFFSET ' + dialect.builder._pushValue(params.offset) + ' rows'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. all words should be in lower case, you use |
||
} | ||
}); | ||
|
||
dialect.blocks.set('returning', function(params) { | ||
var result = dialect.buildBlock('fields', {fields: params.returning}); | ||
|
||
if (result) result = 'output ' + result; | ||
|
||
return result; | ||
}); | ||
|
||
dialect.blocks.set('insert:values', function(params) { | ||
var values = params.values; | ||
|
||
if (!_.isArray(values)) values = [values]; | ||
|
||
var fields = params.fields || _(values) | ||
.chain() | ||
.map(function(row) { | ||
return _(row).keys(); | ||
}) | ||
.flatten() | ||
.uniq() | ||
.value(); | ||
|
||
return dialect.buildTemplate('insertValues', { | ||
fields: fields, | ||
returning: params.returning || undefined, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why |
||
values: _(values).map(function(row) { | ||
return _(fields).map(function(field) { | ||
return dialect.buildBlock('value', {value: row[field]}); | ||
}); | ||
}) | ||
}); | ||
}); | ||
|
||
dialect.blocks.add('insertValues:values', function(params) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is seems that this block is not needed to override |
||
return _(params.values).map(function(row) { | ||
return '(' + row.join(', ') + ')'; | ||
}).join(', '); | ||
}); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,9 +3,47 @@ | |
var BaseDialect = require('../base'); | ||
var _ = require('underscore'); | ||
var util = require('util'); | ||
var templatesInit = require('./templates'); | ||
var blocksInit = require('./blocks'); | ||
|
||
var Dialect = module.exports = function(builder) { | ||
|
||
builder._pushValue = function(value) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You shouldn't override |
||
if (_.isUndefined(value) || _.isNull(value)) { | ||
return 'null'; | ||
} else if (_.isBoolean(value)) { | ||
return String(Number(value)); | ||
} else if (_.isNumber(value)) { | ||
return String(value); | ||
} else if (_.isString(value) || _.isDate(value)) { | ||
if (this.options.separatedValues) { | ||
var placeholder = this._getPlaceholder(); | ||
|
||
if (this.options.namedValues) { | ||
this._values[placeholder] = value; | ||
} else { | ||
this._values.push(value); | ||
} | ||
|
||
return this._wrapPlaceholder(placeholder); | ||
} else { | ||
if (_.isDate(value)) value = value.toISOString(); | ||
|
||
return '\'' + value + '\''; | ||
} | ||
} else { | ||
throw new Error('Wrong value type "' + (typeof value) + '"'); | ||
} | ||
}; | ||
|
||
BaseDialect.call(this, builder); | ||
|
||
// init templates | ||
templatesInit(this); | ||
|
||
// init blocks | ||
blocksInit(this); | ||
|
||
}; | ||
|
||
util.inherits(Dialect, BaseDialect); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
'use strict'; | ||
|
||
var templateChecks = require('../../utils/templateChecks'); | ||
var orRegExp = /^(rollback|abort|replace|fail|ignore)$/i; | ||
|
||
module.exports = function(dialect) { | ||
dialect.templates.set('select', { | ||
pattern: '{with} {withRecursive} select {distinct} {limit} {fields} ' + | ||
'from {from} {table} {query} {select} {expression} {alias} ' + | ||
'{join} {condition} {group} {having} {sort} {offset}', | ||
defaults: { | ||
fields: {} | ||
}, | ||
validate: function(type, params) { | ||
templateChecks.onlyOneOfProps(type, params, ['with', 'withRecursive']); | ||
templateChecks.propType(type, params, 'with', 'object'); | ||
templateChecks.propType(type, params, 'withRecursive', 'object'); | ||
|
||
templateChecks.propType(type, params, 'distinct', 'boolean'); | ||
|
||
templateChecks.propType(type, params, 'fields', ['array', 'object']); | ||
|
||
templateChecks.propType(type, params, 'from', ['string', 'array', 'object']); | ||
|
||
templateChecks.atLeastOneOfProps(type, params, ['table', 'query', 'select', 'expression']); | ||
templateChecks.onlyOneOfProps(type, params, ['table', 'query', 'select', 'expression']); | ||
|
||
templateChecks.propType(type, params, 'table', 'string'); | ||
templateChecks.propType(type, params, 'query', 'object'); | ||
templateChecks.propType(type, params, 'select', 'object'); | ||
templateChecks.propType(type, params, 'expression', ['string', 'object']); | ||
|
||
templateChecks.propType(type, params, 'alias', ['string', 'object']); | ||
|
||
templateChecks.propType(type, params, 'join', ['array', 'object']); | ||
|
||
templateChecks.propType(type, params, 'condition', ['array', 'object']); | ||
templateChecks.propType(type, params, 'having', ['array', 'object']); | ||
|
||
templateChecks.propType(type, params, 'group', ['string', 'array']); | ||
|
||
templateChecks.propType(type, params, 'sort', ['string', 'array', 'object']); | ||
|
||
templateChecks.propType(type, params, 'offset', ['number', 'string']); | ||
templateChecks.propType(type, params, 'limit', ['number', 'string']); | ||
} | ||
}); | ||
|
||
dialect.templates.add('insert', { | ||
pattern: '{with} {withRecursive} insert {or} into {table} {values} ' + | ||
'{condition}', | ||
validate: function(type, params) { | ||
templateChecks.onlyOneOfProps(type, params, ['with', 'withRecursive']); | ||
templateChecks.propType(type, params, 'with', 'object'); | ||
templateChecks.propType(type, params, 'withRecursive', 'object'); | ||
|
||
templateChecks.propType(type, params, 'or', 'string'); | ||
templateChecks.propMatch(type, params, 'or', orRegExp); | ||
|
||
templateChecks.requiredProp(type, params, 'table'); | ||
templateChecks.propType(type, params, 'table', 'string'); | ||
|
||
templateChecks.requiredProp(type, params, 'values'); | ||
templateChecks.propType(type, params, 'values', ['array', 'object']); | ||
|
||
templateChecks.propType(type, params, 'condition', ['array', 'object']); | ||
|
||
} | ||
}); | ||
|
||
dialect.templates.add('insertValues', { | ||
pattern: '({fields}) {returning} values {values}', | ||
validate: function(type, params) { | ||
templateChecks.requiredProp('values', params, 'fields'); | ||
templateChecks.propType('values', params, 'fields', 'array'); | ||
templateChecks.minPropLength('values', params, 'fields', 1); | ||
|
||
templateChecks.propType(type, params, 'returning', ['array', 'object']); | ||
|
||
templateChecks.requiredProp('values', params, 'values'); | ||
templateChecks.propType('values', params, 'values', 'array'); | ||
templateChecks.minPropLength('values', params, 'values', 1); | ||
} | ||
}); | ||
|
||
dialect.templates.add('update', { | ||
pattern: '{with} {withRecursive} update {or} {table} {alias} {modifier} {returning} ' + | ||
'{condition} ', | ||
validate: function(type, params) { | ||
templateChecks.onlyOneOfProps(type, params, ['with', 'withRecursive']); | ||
templateChecks.propType(type, params, 'with', 'object'); | ||
templateChecks.propType(type, params, 'withRecursive', 'object'); | ||
|
||
templateChecks.propType(type, params, 'or', 'string'); | ||
templateChecks.propMatch(type, params, 'or', orRegExp); | ||
|
||
templateChecks.requiredProp(type, params, 'table'); | ||
templateChecks.propType(type, params, 'table', 'string'); | ||
|
||
templateChecks.propType(type, params, 'returning', ['array', 'object']); | ||
|
||
templateChecks.propType(type, params, 'alias', 'string'); | ||
|
||
templateChecks.requiredProp(type, params, 'modifier'); | ||
templateChecks.propType(type, params, 'modifier', 'object'); | ||
|
||
templateChecks.propType(type, params, 'condition', ['array', 'object']); | ||
|
||
} | ||
}); | ||
|
||
dialect.templates.add('remove', { | ||
pattern: '{with} {withRecursive} delete from {table} {returning} {alias} {condition} ', | ||
validate: function(type, params) { | ||
templateChecks.onlyOneOfProps(type, params, ['with', 'withRecursive']); | ||
templateChecks.propType(type, params, 'with', 'object'); | ||
templateChecks.propType(type, params, 'withRecursive', 'object'); | ||
|
||
templateChecks.requiredProp(type, params, 'table'); | ||
templateChecks.propType(type, params, 'table', 'string'); | ||
|
||
templateChecks.propType(type, params, 'returning', ['array', 'object']); | ||
|
||
templateChecks.propType(type, params, 'alias', 'string'); | ||
|
||
templateChecks.propType(type, params, 'condition', ['array', 'object']); | ||
|
||
} | ||
}); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
'use strict'; | ||
|
||
var jsonSql = require('../../lib')({ | ||
dialect: 'mssql', | ||
namedValues: false | ||
}); | ||
var expect = require('chai').expect; | ||
|
||
describe('MSSQL dialect', function() { | ||
describe('limit', function() { | ||
it('should be ok with `limit` property', function() { | ||
var result = jsonSql.build({ | ||
table: 'test', | ||
fields: ['user'], | ||
limit: 1, | ||
condition: { | ||
'name': {$eq: 'test'} | ||
} | ||
}); | ||
expect(result.query).to.be.equal('select top(1) "user" from "test" where "name" = $1;'); | ||
}); | ||
|
||
it('should be ok with `limit` and `offset` properties', function() { | ||
var result = jsonSql.build({ | ||
table: 'test', | ||
fields: ['user'], | ||
limit: 4, | ||
offset: 2, | ||
condition: { | ||
'name': {$eq: 'test'} | ||
} | ||
}); | ||
expect(result.query).to.be.equal('select "user" from "test" where "name" = $1 order by 1' + | ||
' offset 2 rows fetch next 4 rows only;'); | ||
}); | ||
}); | ||
describe('returning', function() { | ||
it('should be ok with `remove` type', function() { | ||
var result = jsonSql.build({ | ||
type: 'remove', | ||
table: 'test', | ||
returning: ['DELETED.*'], | ||
condition: { | ||
Description: {$eq: 'test'} | ||
} | ||
}); | ||
expect(result.query).to.be.equal('delete from "test" output "DELETED".* where ' + | ||
'"Description" = $1;'); | ||
}); | ||
it('should be ok with `insert` type', function() { | ||
var result = jsonSql.build({ | ||
type: 'insert', | ||
table: 'test', | ||
returning: ['INSERTED.*'], | ||
values: { | ||
Description: 'test', | ||
} | ||
}); | ||
expect(result.query).to.be.equal('insert into "test" ("Description") output ' + | ||
'"INSERTED".* values ($1);'); | ||
}); | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe
_.isUndefined
check is better here?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And why you check
offset
value, but returnlimit
value?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
MSSQL has no LIMIT/OFFSET Statement like mysql.
There are 2 diffrent ways:
SELECT TOP X Fields... is the same as LIMIT.
But you cannot use OFFSET together with TOP. IN MSSQL 2012 and higher a new statement was added.
SELECT fields from ... ORDER BY A OFFSET Y ROWS FETCH NEXT X ROWS ONLY
That is why he checks for the params.offset. If it is used the top() statement will be removed and the offset/fetch statement will include the limit.