-
Notifications
You must be signed in to change notification settings - Fork 279
/
expression-builder.ts
134 lines (126 loc) · 3.97 KB
/
expression-builder.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
import { QueryBuilder } from './query-builder.js'
import { SelectQueryNode } from '../operation-node/select-query-node.js'
import {
parseTableExpressionOrList,
TableExpression,
QueryBuilderWithTable,
TableExpressionOrList,
} from '../parser/table-parser.js'
import { NoopQueryExecutor } from '../query-executor/noop-query-executor.js'
import { WithSchemaPlugin } from '../plugin/with-schema/with-schema-plugin.js'
import { createQueryId } from '../util/query-id.js'
import { freeze } from '../util/object-utils.js'
import { ParseContext } from '../parser/parse-context.js'
import { FunctionBuilder } from './function-builder.js'
export class ExpressionBuilder<DB, TB extends keyof DB> {
readonly #props: ExpressionBuilderProps
constructor(props: ExpressionBuilderProps) {
this.#props = freeze(props)
}
/**
* Returns a {@link FunctionBuilder} that can be used to write type safe function
* calls.
*
* The difference between this and {@link Kysely.fn} is that this one is more
* type safe. You can only refer to columns visible to the part of the query
* you are building. {@link Kysely.fn} allows you to refer to columns in any
* table of the database even if it doesn't produce valid SQL.
*
* ```ts
* await db.selectFrom('person')
* .innerJoin('pet', 'pet.owner_id', 'person.id')
* .select([
* 'person.id',
* (qb) => qb.fn.count('pet.id').as('pet_count')
* ])
* .groupBy('person.id')
* .having(count('pet.id'), '>', 10)
* .execute()
* ```
*
* The generated SQL (postgresql):
*
* ```sql
* select "person"."id", count("pet"."id") as "pet_count"
* from "person"
* inner join "pet" on "pet"."owner_id" = "person"."id"
* group by "person"."id"
* having count("pet"."id") > $1
* ```
*/
get fn(): FunctionBuilder<DB, TB> {
return new FunctionBuilder({ executor: this.#props.executor })
}
/**
* Creates a subquery.
*
* The query builder returned by this method is typed in a way that you can refer to
* all tables of the parent query in addition to the subquery's tables.
*
* @example
* This example shows that you can refer to both `pet.owner_id` and `person.id`
* columns from the subquery. This is needed to be able to create correlated
* subqueries:
*
* ```ts
* const result = await db.selectFrom('pet')
* .select([
* 'pet.name',
* (qb) => qb.subQuery('person')
* .whereRef('person.id', '=', 'pet.owner_id')
* .select('person.first_name')
* .as('owner_name')
* ])
* .execute()
*
* console.log(result[0].owner_name)
* ```
*
* The generated SQL (postgresql):
*
* ```sql
* select
* "pet"."name",
* ( select "person"."first_name"
* from "person"
* where "person"."id" = "pet"."owner_id"
* ) as "owner_name"
* from "pet"
* ```
*
* You can use a normal query in place of `(qb) => qb.subQuery(...)` but in
* that case Kysely typings wouldn't allow you to reference `pet.owner_id`
* because `pet` is not joined to that query.
*/
subQuery<F extends TableExpression<DB, TB>>(
from: F[]
): QueryBuilderWithTable<DB, TB, {}, F>
subQuery<F extends TableExpression<DB, TB>>(
from: F
): QueryBuilderWithTable<DB, TB, {}, F>
subQuery(table: TableExpressionOrList<DB, TB>): any {
return new QueryBuilder({
queryId: createQueryId(),
executor: this.#props.executor,
parseContext: this.#props.parseContext,
queryNode: SelectQueryNode.create(
parseTableExpressionOrList(this.#props.parseContext, table)
),
})
}
/**
* See {@link QueryCreator.withSchema}
*/
withSchema(schema: string): ExpressionBuilder<DB, TB> {
return new ExpressionBuilder({
...this.#props,
executor: this.#props.executor.withPluginAtFront(
new WithSchemaPlugin(schema)
),
})
}
}
export interface ExpressionBuilderProps {
readonly executor: NoopQueryExecutor
readonly parseContext: ParseContext
}