From 2d0459111d45ac83b8d738626bd5717a979caea9 Mon Sep 17 00:00:00 2001 From: EthanHur Date: Thu, 24 Oct 2019 23:08:12 +0900 Subject: [PATCH] orderBy accepts QueryBuilder (#3491) --- lib/query/compiler.js | 17 +++++++++++++---- test/unit/query/builder.js | 37 +++++++++++++++++++++++++++++++++++++ types/index.d.ts | 3 +++ types/test.ts | 10 ++++++++++ 4 files changed, 63 insertions(+), 4 deletions(-) diff --git a/lib/query/compiler.js b/lib/query/compiler.js index f31de0af04..89170cb1ee 100644 --- a/lib/query/compiler.js +++ b/lib/query/compiler.js @@ -2,6 +2,7 @@ // ------- const helpers = require('../helpers'); const Raw = require('../raw'); +const QueryBuilder = require('./builder'); const JoinClause = require('./joinclause'); const debug = require('debug'); @@ -823,16 +824,24 @@ assign(QueryCompiler.prototype, { return vals; }, + _formatGroupsItemValue(value) { + const { formatter } = this; + if (value instanceof Raw) { + return formatter.unwrapRaw(value); + } else if (value instanceof QueryBuilder) { + return '(' + formatter.columnize(value) + ')'; + } else { + return formatter.columnize(value); + } + }, + // Compiles the `order by` statements. _groupsOrders(type) { const items = this.grouped[type]; if (!items) return ''; const { formatter } = this; const sql = items.map((item) => { - const column = - item.value instanceof Raw - ? formatter.unwrapRaw(item.value) - : formatter.columnize(item.value); + const column = this._formatGroupsItemValue(item.value); const direction = type === 'order' && item.type !== 'orderByRaw' ? ` ${formatter.direction(item.direction)}` diff --git a/test/unit/query/builder.js b/test/unit/query/builder.js index eba8f0867d..2f91e1bcfc 100644 --- a/test/unit/query/builder.js +++ b/test/unit/query/builder.js @@ -3176,6 +3176,43 @@ describe('QueryBuilder', () => { ); }); + it('order by accepts query builder', () => { + testsql( + qb() + .select() + .from('persons') + .orderBy( + qb() + .select() + .from('persons as p') + .whereColumn('persons.id', 'p.id') + .select('p.id') + ), + { + mysql: { + sql: + 'select * from `persons` order by (select `p`.`id` from `persons` as `p` where `persons`.`id` = `p`.`id`) asc', + bindings: [], + }, + mssql: { + sql: + 'select * from [persons] order by (select [p].[id] from [persons] as [p] where [persons].[id] = [p].[id]) asc', + bindings: [], + }, + pg: { + sql: + 'select * from "persons" order by (select "p"."id" from "persons" as "p" where "persons"."id" = "p"."id") asc', + bindings: [], + }, + sqlite3: { + sql: + 'select * from `persons` order by (select `p`.`id` from `persons` as `p` where `persons`.`id` = `p`.`id`) asc', + bindings: [], + }, + } + ); + }); + it('raw group bys', () => { testsql( qb() diff --git a/types/index.d.ts b/types/index.d.ts index 7d8102ebcb..c671d99649 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -1197,6 +1197,9 @@ declare namespace Knex { ( columnDefs: Array ): QueryBuilder; + ( + subQueryBuilder: QueryBuilder + ): QueryBuilder; } interface Intersect { diff --git a/types/test.ts b/types/test.ts index 024fea4f00..44fe5aa366 100644 --- a/types/test.ts +++ b/types/test.ts @@ -492,6 +492,16 @@ const main = async () => { .orderBy('name', 'desc') .havingRaw('age > ?', [10]); + // $ExpectType User[] + await knex('users') + .select() + .orderBy( + knex('users') + .select('u.id') + .from('users as u') + .where('users.id', 'u.id') + ); + // $ExpectType Dict[] await knex('users').count();