Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 6ab8384

Browse files
committed
fixes Vincit#1131
1 parent afde81c commit 6ab8384

File tree

7 files changed

+196
-54
lines changed

7 files changed

+196
-54
lines changed

lib/queryBuilder/QueryBuilder.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1271,13 +1271,13 @@ function buildInto(builder, knexBuilder) {
12711271
const hasSelects = builder.has(QueryBuilderBase.SelectSelector);
12721272

12731273
// Set the table only if it hasn't been explicitly set yet.
1274-
if (!fromOperation) {
1274+
if (!builder.isPartialQuery() && !fromOperation) {
12751275
setDefaultTable(builder, knexBuilder);
12761276
}
12771277

12781278
// Only add `table.*` select if there are no explicit selects
12791279
// and `from` is a table name and not a subquery.
1280-
if (!hasSelects && (!fromOperation || fromOperation.table)) {
1280+
if (!builder.isPartialQuery() && !hasSelects && (!fromOperation || fromOperation.table)) {
12811281
setDefaultSelect(builder, knexBuilder);
12821282
}
12831283

lib/queryBuilder/QueryBuilderBase.js

Lines changed: 0 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -13,33 +13,7 @@ const WhereJsonPostgresOperation = require('./operations/jsonApi/WhereJsonPostgr
1313
const WhereJsonHasPostgresOperation = require('./operations/jsonApi/WhereJsonHasPostgresOperation');
1414
const WhereJsonNotObjectPostgresOperation = require('./operations/jsonApi/WhereJsonNotObjectPostgresOperation');
1515

16-
const SelectSelector = SelectOperation;
17-
const WhereSelector = /^(where|orWhere|andWhere)/;
18-
const OrderBySelector = /orderBy/;
19-
const JoinSelector = /(join|joinRaw)$/i;
20-
const FromSelector = /^(from|into|table)$/;
21-
2216
class QueryBuilderBase extends QueryBuilderOperationSupport {
23-
static get SelectSelector() {
24-
return SelectSelector;
25-
}
26-
27-
static get WhereSelector() {
28-
return WhereSelector;
29-
}
30-
31-
static get JoinSelector() {
32-
return JoinSelector;
33-
}
34-
35-
static get FromSelector() {
36-
return FromSelector;
37-
}
38-
39-
static get OrderBySelector() {
40-
return OrderBySelector;
41-
}
42-
4317
modify() {
4418
const func = arguments[0];
4519

@@ -68,18 +42,6 @@ class QueryBuilderBase extends QueryBuilderOperationSupport {
6842
return this;
6943
}
7044

71-
clearSelect() {
72-
return this.clear(SelectSelector);
73-
}
74-
75-
clearWhere() {
76-
return this.clear(WhereSelector);
77-
}
78-
79-
clearOrder() {
80-
return this.clear(OrderBySelector);
81-
}
82-
8345
select() {
8446
return this.addOperation(new SelectOperation('select'), arguments);
8547
}

lib/queryBuilder/QueryBuilderOperationSupport.js

Lines changed: 63 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,20 @@ const { isString, mergeMaps } = require('../utils/objectUtils');
33
const QueryBuilderContextBase = require('./QueryBuilderContextBase');
44
const QueryBuilderUserContext = require('./QueryBuilderUserContext');
55

6+
const SelectSelector = /^(select|columns|column|distinct|count|countDistinct|min|max|sum|sumDistinct|avg|avgDistinct)$/;
7+
const WhereSelector = /^(where|orWhere|andWhere)/;
8+
const OnSelector = /^(on|orOn|andOn)/;
9+
const OrderBySelector = /orderBy/;
10+
const JoinSelector = /(join|joinRaw)$/i;
11+
const FromSelector = /^(from|into|table)$/;
12+
613
class QueryBuilderOperationSupport {
714
constructor(knex) {
815
this._knex = knex;
916
this._operations = [];
1017
this._context = new this.constructor.QueryBuilderContext(this);
1118
this._parentQuery = null;
19+
this._isPartialQuery = false;
1220
}
1321

1422
static get QueryBuilderContext() {
@@ -19,6 +27,42 @@ class QueryBuilderOperationSupport {
1927
return QueryBuilderUserContext;
2028
}
2129

30+
static get SelectSelector() {
31+
return SelectSelector;
32+
}
33+
34+
static get WhereSelector() {
35+
return WhereSelector;
36+
}
37+
38+
static get OnSelector() {
39+
return OnSelector;
40+
}
41+
42+
static get JoinSelector() {
43+
return JoinSelector;
44+
}
45+
46+
static get FromSelector() {
47+
return FromSelector;
48+
}
49+
50+
static get OrderBySelector() {
51+
return OrderBySelector;
52+
}
53+
54+
clearSelect() {
55+
return this.clear(SelectSelector);
56+
}
57+
58+
clearWhere() {
59+
return this.clear(WhereSelector);
60+
}
61+
62+
clearOrder() {
63+
return this.clear(OrderBySelector);
64+
}
65+
2266
context(obj) {
2367
const ctx = this._context;
2468

@@ -55,6 +99,15 @@ class QueryBuilderOperationSupport {
5599
}
56100
}
57101

102+
isPartialQuery(isPartialQuery) {
103+
if (arguments.length === 0) {
104+
return this._isPartialQuery;
105+
} else {
106+
this._isPartialQuery = isPartialQuery;
107+
return this;
108+
}
109+
}
110+
58111
tableNameFor(tableName, newTableName) {
59112
const ctx = this.internalContext();
60113
const tableMap = ctx.tableMap;
@@ -106,15 +159,18 @@ class QueryBuilderOperationSupport {
106159
}
107160

108161
subqueryOf(query) {
109-
const ctx = query.internalContext();
162+
if (query) {
163+
const ctx = this.internalContext();
110164

111-
// Merge alias and table name maps for subqueries.
112-
ctx.aliasMap = mergeMaps(query.internalContext().aliasMap, ctx.aliasMap);
113-
ctx.tableMap = mergeMaps(query.internalContext().tableMap, ctx.tableMap);
165+
// Merge alias and table name maps for subqueries.
166+
ctx.aliasMap = mergeMaps(query.internalContext().aliasMap, ctx.aliasMap);
167+
ctx.tableMap = mergeMaps(query.internalContext().tableMap, ctx.tableMap);
114168

115-
if (query) {
116169
this._parentQuery = query;
117-
this.knex(query.unsafeKnex());
170+
171+
if (this.unsafeKnex() === null) {
172+
this.knex(query.unsafeKnex());
173+
}
118174
}
119175

120176
return this;
@@ -259,6 +315,7 @@ class QueryBuilderOperationSupport {
259315
builder._operations = this._operations.slice();
260316
builder._context = this._context.clone();
261317
builder._parentQuery = this._parentQuery;
318+
builder._isPartialQuery = this._isPartialQuery;
262319

263320
return builder;
264321
}

lib/queryBuilder/operations/ObjectionToKnexConvertingOperation.js

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ const QueryBuilderOperation = require('./QueryBuilderOperation');
44
const { isPlainObject, isObject, isFunction } = require('../../utils/objectUtils');
55
const { isKnexQueryBuilder, isKnexJoinBuilder } = require('../../utils/knexUtils');
66

7-
const getQueryBuilderBase = once(() => require('../QueryBuilderBase'));
87
const getJoinBuilder = once(() => require('../JoinBuilder'));
98

109
// An abstract operation base class that converts all arguments from objection types
@@ -13,16 +12,18 @@ const getJoinBuilder = once(() => require('../JoinBuilder'));
1312
class ObjectionToKnexConvertingOperation extends QueryBuilderOperation {
1413
constructor(name, opt) {
1514
super(name, opt);
15+
this.rawArgs = null;
1616
this.args = null;
1717
}
1818

1919
onAdd(builder, args) {
20+
this.rawArgs = args;
2021
this.args = args;
2122
return shouldBeAdded(this.name, builder, args);
2223
}
2324

2425
onBuild(builder) {
25-
this.args = convertArgs(this.name, builder, this.args);
26+
this.args = convertArgs(this.name, builder, this.rawArgs);
2627
}
2728
}
2829

@@ -134,20 +135,21 @@ function convertFunction(func, builder) {
134135
}
135136

136137
function convertQueryBuilderFunction(knexQueryBuilder, func, builder) {
137-
const QueryBuilderBase = getQueryBuilderBase();
138-
const convertedQueryBuilder = new QueryBuilderBase(builder.knex());
138+
const convertedQueryBuilder = builder.constructor.forClass(builder.modelClass());
139139

140-
convertedQueryBuilder.subqueryOf(builder);
140+
convertedQueryBuilder.subqueryOf(builder).isPartialQuery(true);
141141
func.call(convertedQueryBuilder, convertedQueryBuilder);
142-
convertedQueryBuilder.buildInto(knexQueryBuilder);
142+
143+
convertedQueryBuilder.build(knexQueryBuilder);
143144
}
144145

145146
function convertJoinBuilderFunction(knexJoinBuilder, func, builder) {
146147
const JoinBuilder = getJoinBuilder();
147148
const joinClauseBuilder = new JoinBuilder(builder.knex());
148149

149-
joinClauseBuilder.subqueryOf(builder);
150+
joinClauseBuilder.subqueryOf(builder).isPartialQuery(true);
150151
func.call(joinClauseBuilder, joinClauseBuilder);
152+
151153
joinClauseBuilder.buildInto(knexJoinBuilder);
152154
}
153155

lib/relations/RelationSubqueryOperation.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,23 @@ class RelationSubqueryOperation extends QueryBuilderOperation {
99
onBuild(builder) {
1010
return this.relation.findQuery(builder, {
1111
isColumnRef: true,
12-
ownerIds: this.relation.ownerProp.refs(builder.parentQuery())
12+
ownerIds: this.relation.ownerProp.refs(findFirstNonPartialAncestorQuery(builder))
1313
});
1414
}
1515
}
1616

17+
function findFirstNonPartialAncestorQuery(builder) {
18+
builder = builder.parentQuery();
19+
20+
while (builder.isPartialQuery()) {
21+
if (!builder.parentQuery()) {
22+
break;
23+
}
24+
25+
builder = builder.parentQuery();
26+
}
27+
28+
return builder;
29+
}
30+
1731
module.exports = RelationSubqueryOperation;

tests/integration/find.js

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -657,6 +657,27 @@ module.exports = session => {
657657
});
658658
});
659659

660+
it('knex-style function subqueries', () => {
661+
return Model1.query()
662+
.from(builder =>
663+
builder
664+
.select('model2.*')
665+
.from('model2')
666+
.as('foo')
667+
)
668+
.orderBy(builder =>
669+
builder
670+
.from('model2')
671+
.where('model2.id_col', ref('foo.id_col'))
672+
.select('model2_prop2')
673+
)
674+
.whereExists(builder => builder.from('Model1').where('foo.model1_id', ref('Model1.id')))
675+
.castTo(Model2)
676+
.then(models => {
677+
expect(models.map(it => it.model2Prop2)).to.eql([10, 20, 30]);
678+
});
679+
});
680+
660681
it('raw in select', () => {
661682
return Model2.query()
662683
.select('model2.*', raw('?? + ? as ??', 'model2_prop2', 10, 'model2_prop2'))
@@ -812,6 +833,66 @@ module.exports = session => {
812833
});
813834
});
814835

836+
it('deeply nested where subquery to same table with alias', () => {
837+
return Model1.query()
838+
.upsertGraph(
839+
{
840+
id: 1,
841+
model1Relation1: {
842+
id: 2
843+
}
844+
},
845+
{ relate: true }
846+
)
847+
.then(() => {
848+
return Model1.query()
849+
.alias('m1')
850+
.where(builder => {
851+
// The two nested grouping where's are relevant here.
852+
builder.where(builder => {
853+
builder.where(lit(2), Model1.relatedQuery('model1Relation1').select('id'));
854+
});
855+
});
856+
})
857+
.then(res => {
858+
expect(res).to.have.length(1);
859+
expect(res[0].id).to.equal(1);
860+
});
861+
});
862+
863+
it('deeply nested subqueries to same table with alias', () => {
864+
return Model1.query()
865+
.insertGraph({
866+
id: 1001,
867+
model1Relation1: {
868+
id: 1002,
869+
model1Relation1: {
870+
id: 1003,
871+
model1Relation1: {
872+
id: 1004
873+
}
874+
}
875+
}
876+
})
877+
.then(() => {
878+
return Model1.query()
879+
.alias('m1')
880+
.whereExists(
881+
Model1.relatedQuery('model1Relation1')
882+
.alias('m2')
883+
.whereExists(
884+
Model1.relatedQuery('model1Relation1')
885+
.alias('m3')
886+
.whereExists(Model1.relatedQuery('model1Relation1').alias('m4'))
887+
)
888+
);
889+
})
890+
.then(res => {
891+
expect(res).to.have.length(1);
892+
expect(res[0].id).to.equal(1001);
893+
});
894+
});
895+
815896
it('.modify()', () => {
816897
let builder = Model2.query();
817898

tests/unit/queryBuilder/QueryBuilder.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1533,6 +1533,32 @@ describe('QueryBuilder', () => {
15331533
expect(query.tableRefFor(TestModel)).to.equal('Lyl');
15341534
});
15351535

1536+
it('should use Model.QueryBuilder in builder methods', () => {
1537+
class CustomQueryBuilder extends TestModel.QueryBuilder {}
1538+
1539+
TestModel.QueryBuilder = CustomQueryBuilder;
1540+
const checks = [];
1541+
1542+
return TestModel.query()
1543+
.select('*', builder => {
1544+
checks.push(builder instanceof CustomQueryBuilder);
1545+
})
1546+
.where(builder => {
1547+
checks.push(builder instanceof CustomQueryBuilder);
1548+
1549+
builder.where(builder => {
1550+
checks.push(builder instanceof CustomQueryBuilder);
1551+
});
1552+
})
1553+
.modify(builder => {
1554+
checks.push(builder instanceof CustomQueryBuilder);
1555+
})
1556+
.then(() => {
1557+
expect(checks).to.have.length(4);
1558+
expect(checks.every(it => it)).to.equal(true);
1559+
});
1560+
});
1561+
15361562
describe('eager, allowEager, and mergeAllowEager', () => {
15371563
beforeEach(() => {
15381564
const rel = {

0 commit comments

Comments
 (0)