Skip to content
This repository has been archived by the owner on Nov 9, 2021. It is now read-only.

Commit

Permalink
Add Postgres DISTINCT ON syntax (#252)
Browse files Browse the repository at this point in the history
  • Loading branch information
saltire authored and hiddentao committed Jul 12, 2016
1 parent 853c475 commit 8093a5e
Show file tree
Hide file tree
Showing 6 changed files with 168 additions and 44 deletions.
18 changes: 9 additions & 9 deletions dist/squel-basic.js
Expand Up @@ -460,16 +460,16 @@ function _buildSquel() {
if (null === item) {
// null is allowed
} else if ("string" === itemType || "number" === itemType || "boolean" === itemType) {
// primitives are allowed
} else if (item instanceof cls.BaseBuilder) {
// Builders allowed
} else {
var typeIsValid = !!getValueHandler(item, this.options.valueHandlers, cls.globalValueHandlers);
// primitives are allowed
} else if (item instanceof cls.BaseBuilder) {
// Builders allowed
} else {
var typeIsValid = !!getValueHandler(item, this.options.valueHandlers, cls.globalValueHandlers);

if (!typeIsValid) {
throw new Error("field value must be a string, number, boolean, null or one of the registered custom value types");
}
}
if (!typeIsValid) {
throw new Error("field value must be a string, number, boolean, null or one of the registered custom value types");
}
}

return item;
}
Expand Down
2 changes: 1 addition & 1 deletion dist/squel-basic.min.js

Large diffs are not rendered by default.

113 changes: 83 additions & 30 deletions dist/squel.js
Expand Up @@ -460,16 +460,16 @@ function _buildSquel() {
if (null === item) {
// null is allowed
} else if ("string" === itemType || "number" === itemType || "boolean" === itemType) {
// primitives are allowed
} else if (item instanceof cls.BaseBuilder) {
// Builders allowed
} else {
var typeIsValid = !!getValueHandler(item, this.options.valueHandlers, cls.globalValueHandlers);
// primitives are allowed
} else if (item instanceof cls.BaseBuilder) {
// Builders allowed
} else {
var typeIsValid = !!getValueHandler(item, this.options.valueHandlers, cls.globalValueHandlers);

if (!typeIsValid) {
throw new Error("field value must be a string, number, boolean, null or one of the registered custom value types");
}
}
if (!typeIsValid) {
throw new Error("field value must be a string, number, boolean, null or one of the registered custom value types");
}
}

return item;
}
Expand Down Expand Up @@ -3634,74 +3634,127 @@ squel.flavours['postgres'] = function (_squel) {
return _class48;
}(cls.Block);

// SELECT query builder.
cls.Select = function (_cls$QueryBuilder10) {
_inherits(_class49, _cls$QueryBuilder10);
// DISTINCT [ON]
cls.DistinctOnBlock = function (_cls$Block21) {
_inherits(_class49, _cls$Block21);

function _class49(options) {
var blocks = arguments.length <= 1 || arguments[1] === undefined ? null : arguments[1];

_classCallCheck(this, _class49);

blocks = blocks || [new cls.WithBlock(options), new cls.StringBlock(options, 'SELECT'), new cls.FunctionBlock(options), new cls.DistinctBlock(options), new cls.GetFieldBlock(options), new cls.FromTableBlock(options), new cls.JoinBlock(options), new cls.WhereBlock(options), new cls.GroupByBlock(options), new cls.HavingBlock(options), new cls.OrderByBlock(options), new cls.LimitBlock(options), new cls.OffsetBlock(options), new cls.UnionBlock(options)];
var _this55 = _possibleConstructorReturn(this, Object.getPrototypeOf(_class49).call(this, options));

return _possibleConstructorReturn(this, Object.getPrototypeOf(_class49).call(this, options, blocks));
_this55._distinctFields = [];
return _this55;
}

_createClass(_class49, [{
key: 'distinct',
value: function distinct() {
var _this56 = this;

this._useDistinct = true;

// Add all fields to the DISTINCT ON clause.

for (var _len12 = arguments.length, fields = Array(_len12), _key12 = 0; _key12 < _len12; _key12++) {
fields[_key12] = arguments[_key12];
}

fields.forEach(function (field) {
_this56._distinctFields.push(_this56._sanitizeField(field));
});
}
}, {
key: '_toParamString',
value: function _toParamString() {
var text = '';

if (this._useDistinct) {
text = 'DISTINCT';

if (this._distinctFields.length) {
text += ' ON (' + this._distinctFields.join(', ') + ')';
}
}

return {
text: text,
values: []
};
}
}]);

return _class49;
}(cls.QueryBuilder);
}(cls.Block);

// INSERT query builder
cls.Insert = function (_cls$QueryBuilder11) {
_inherits(_class50, _cls$QueryBuilder11);
// SELECT query builder.
cls.Select = function (_cls$QueryBuilder10) {
_inherits(_class50, _cls$QueryBuilder10);

function _class50(options) {
var blocks = arguments.length <= 1 || arguments[1] === undefined ? null : arguments[1];

_classCallCheck(this, _class50);

blocks = blocks || [new cls.WithBlock(options), new cls.StringBlock(options, 'INSERT'), new cls.IntoTableBlock(options), new cls.InsertFieldValueBlock(options), new cls.InsertFieldsFromQueryBlock(options), new cls.ReturningBlock(options)];
blocks = blocks || [new cls.WithBlock(options), new cls.StringBlock(options, 'SELECT'), new cls.FunctionBlock(options), new cls.DistinctOnBlock(options), new cls.GetFieldBlock(options), new cls.FromTableBlock(options), new cls.JoinBlock(options), new cls.WhereBlock(options), new cls.GroupByBlock(options), new cls.HavingBlock(options), new cls.OrderByBlock(options), new cls.LimitBlock(options), new cls.OffsetBlock(options), new cls.UnionBlock(options)];

return _possibleConstructorReturn(this, Object.getPrototypeOf(_class50).call(this, options, blocks));
}

return _class50;
}(cls.QueryBuilder);

// UPDATE query builder
cls.Update = function (_cls$QueryBuilder12) {
_inherits(_class51, _cls$QueryBuilder12);
// INSERT query builder
cls.Insert = function (_cls$QueryBuilder11) {
_inherits(_class51, _cls$QueryBuilder11);

function _class51(options) {
var blocks = arguments.length <= 1 || arguments[1] === undefined ? null : arguments[1];

_classCallCheck(this, _class51);

blocks = blocks || [new cls.WithBlock(options), new cls.StringBlock(options, 'UPDATE'), new cls.UpdateTableBlock(options), new cls.SetFieldBlock(options), new cls.FromTableBlock(options), new cls.WhereBlock(options), new cls.OrderByBlock(options), new cls.LimitBlock(options), new cls.ReturningBlock(options)];
blocks = blocks || [new cls.WithBlock(options), new cls.StringBlock(options, 'INSERT'), new cls.IntoTableBlock(options), new cls.InsertFieldValueBlock(options), new cls.InsertFieldsFromQueryBlock(options), new cls.ReturningBlock(options)];

return _possibleConstructorReturn(this, Object.getPrototypeOf(_class51).call(this, options, blocks));
}

return _class51;
}(cls.QueryBuilder);

// DELETE query builder
cls.Delete = function (_cls$QueryBuilder13) {
_inherits(_class52, _cls$QueryBuilder13);
// UPDATE query builder
cls.Update = function (_cls$QueryBuilder12) {
_inherits(_class52, _cls$QueryBuilder12);

function _class52(options) {
var blocks = arguments.length <= 1 || arguments[1] === undefined ? null : arguments[1];

_classCallCheck(this, _class52);

blocks = blocks || [new cls.WithBlock(options), new cls.StringBlock(options, 'UPDATE'), new cls.UpdateTableBlock(options), new cls.SetFieldBlock(options), new cls.FromTableBlock(options), new cls.WhereBlock(options), new cls.OrderByBlock(options), new cls.LimitBlock(options), new cls.ReturningBlock(options)];

return _possibleConstructorReturn(this, Object.getPrototypeOf(_class52).call(this, options, blocks));
}

return _class52;
}(cls.QueryBuilder);

// DELETE query builder
cls.Delete = function (_cls$QueryBuilder13) {
_inherits(_class53, _cls$QueryBuilder13);

function _class53(options) {
var blocks = arguments.length <= 1 || arguments[1] === undefined ? null : arguments[1];

_classCallCheck(this, _class53);

blocks = blocks || [new cls.WithBlock(options), new cls.StringBlock(options, 'DELETE'), new cls.TargetTableBlock(options), new cls.FromTableBlock(_extend({}, options, {
singleTable: true
})), new cls.JoinBlock(options), new cls.WhereBlock(options), new cls.OrderByBlock(options), new cls.LimitBlock(options), new cls.ReturningBlock(options)];

return _possibleConstructorReturn(this, Object.getPrototypeOf(_class52).call(this, options, blocks));
return _possibleConstructorReturn(this, Object.getPrototypeOf(_class53).call(this, options, blocks));
}

return _class52;
return _class53;
}(cls.QueryBuilder);
};
return squel;
Expand Down
4 changes: 2 additions & 2 deletions dist/squel.min.js

Large diffs are not rendered by default.

38 changes: 36 additions & 2 deletions src/postgres.js
Expand Up @@ -58,14 +58,49 @@ squel.flavours['postgres'] = function(_squel) {
}
}

// DISTINCT [ON]
cls.DistinctOnBlock = class extends cls.Block {
constructor(options) {
super(options);

this._distinctFields = [];
}

distinct(...fields) {
this._useDistinct = true;

// Add all fields to the DISTINCT ON clause.
fields.forEach((field) => {
this._distinctFields.push(this._sanitizeField(field));
});
}

_toParamString() {
let text = '';

if (this._useDistinct) {
text = 'DISTINCT';

if (this._distinctFields.length) {
text += ` ON (${this._distinctFields.join(', ')})`;
}
}

return {
text,
values: []
};
}
}

// SELECT query builder.
cls.Select = class extends cls.QueryBuilder {
constructor (options, blocks = null) {
blocks = blocks || [
new cls.WithBlock(options),
new cls.StringBlock(options, 'SELECT'),
new cls.FunctionBlock(options),
new cls.DistinctBlock(options),
new cls.DistinctOnBlock(options),
new cls.GetFieldBlock(options),
new cls.FromTableBlock(options),
new cls.JoinBlock(options),
Expand Down Expand Up @@ -138,4 +173,3 @@ squel.flavours['postgres'] = function(_squel) {
}
}
}

37 changes: 37 additions & 0 deletions test/postgres.test.coffee
Expand Up @@ -135,6 +135,43 @@ test['Postgres flavour'] =
"values": [2]
}

'distinct queries':
beforeEach: ->
@sel.fields(['field1', 'field2']).from('table1')

'>> from(table).distinct()':
beforeEach: ->
@sel.distinct()
toString: ->
assert.same @sel.toString(), 'SELECT DISTINCT field1, field2 FROM table1'
toParam: ->
assert.same @sel.toParam(), {
'text': 'SELECT DISTINCT field1, field2 FROM table1',
'values': []
}

'>> from(table).distinct(field1)':
beforeEach: ->
@sel.distinct('field1')
toString: ->
assert.same @sel.toString(), 'SELECT DISTINCT ON (field1) field1, field2 FROM table1'
toParam: ->
assert.same @sel.toParam(), {
'text': 'SELECT DISTINCT ON (field1) field1, field2 FROM table1',
'values': []
}

'>> from(table).distinct(field1, field2)':
beforeEach: ->
@sel.distinct('field1', 'field2')
toString: ->
assert.same @sel.toString(), 'SELECT DISTINCT ON (field1, field2) field1, field2 FROM table1'
toParam: ->
assert.same @sel.toParam(), {
'text': 'SELECT DISTINCT ON (field1, field2) field1, field2 FROM table1',
'values': []
}

'cte queries':
beforeEach: ->
@sel = squel.select()
Expand Down

0 comments on commit 8093a5e

Please sign in to comment.