Skip to content

Commit d54eaa5

Browse files
committed
add modifyFront and modifyEnd methods. Closes #72
1 parent 6992fc1 commit d54eaa5

File tree

10 files changed

+356
-141
lines changed

10 files changed

+356
-141
lines changed

src/operation-node/operation-node-transformer.ts

Lines changed: 91 additions & 68 deletions
Large diffs are not rendered by default.

src/operation-node/operation-node-visitor.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ import { DefaultValueNode } from './default-value-node.js'
6969
import { freeze } from '../util/object-utils.js'
7070
import { OnNode } from './on-node.js'
7171
import { ValuesNode } from './values-node.js'
72+
import { SelectModifierNode } from './select-modifier-node.js'
7273

7374
export abstract class OperationNodeVisitor {
7475
protected readonly nodeStack: OperationNode[] = []
@@ -145,6 +146,7 @@ export abstract class OperationNodeVisitor {
145146
DefaultValueNode: this.visitDefaultValue.bind(this),
146147
OnNode: this.visitOn.bind(this),
147148
ValuesNode: this.visitValues.bind(this),
149+
SelectModifierNode: this.visitSelectModifier.bind(this),
148150
})
149151

150152
protected readonly visitNode = (node: OperationNode): void => {
@@ -227,4 +229,5 @@ export abstract class OperationNodeVisitor {
227229
protected abstract visitDefaultValue(node: DefaultValueNode): void
228230
protected abstract visitOn(node: OnNode): void
229231
protected abstract visitValues(node: ValuesNode): void
232+
protected abstract visitSelectModifier(node: SelectModifierNode): void
230233
}

src/operation-node/operation-node.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ export type OperationNodeKind =
6565
| 'OnNode'
6666
| 'ValuesNode'
6767
| 'CommonTableExpressionNameNode'
68+
| 'SelectModifierNode'
6869

6970
export interface OperationNode {
7071
readonly kind: OperationNodeKind
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { OperationNode } from './operation-node.js'
2+
import { freeze } from '../util/object-utils.js'
3+
import { RawNode } from './raw-node.js'
4+
5+
export type SelectModifier =
6+
| 'ForUpdate'
7+
| 'ForNoKeyUpdate'
8+
| 'ForShare'
9+
| 'ForKeyShare'
10+
| 'NoWait'
11+
| 'SkipLocked'
12+
| 'Distinct'
13+
14+
export interface SelectModifierNode extends OperationNode {
15+
readonly kind: 'SelectModifierNode'
16+
readonly modifier?: SelectModifier
17+
readonly rawModifier?: RawNode
18+
}
19+
20+
/**
21+
* @internal
22+
*/
23+
export const SelectModifierNode = freeze({
24+
is(node: OperationNode): node is SelectModifierNode {
25+
return node.kind === 'SelectModifierNode'
26+
},
27+
28+
create(modifier: SelectModifier): SelectModifierNode {
29+
return freeze({
30+
kind: 'SelectModifierNode',
31+
modifier,
32+
})
33+
},
34+
35+
createWithRaw(modifier: RawNode): SelectModifierNode {
36+
return freeze({
37+
kind: 'SelectModifierNode',
38+
rawModifier: modifier,
39+
})
40+
},
41+
})

src/operation-node/select-query-node.ts

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -17,26 +17,19 @@ import { SelectionNode } from './selection-node.js'
1717
import { WhereNode } from './where-node.js'
1818
import { WithNode } from './with-node.js'
1919
import { UnionNode } from './union-node.js'
20-
21-
export type SelectModifier =
22-
| 'ForUpdate'
23-
| 'ForNoKeyUpdate'
24-
| 'ForShare'
25-
| 'ForKeyShare'
26-
| 'NoWait'
27-
| 'SkipLocked'
20+
import { SelectModifierNode } from './select-modifier-node.js'
2821

2922
export interface SelectQueryNode extends OperationNode {
3023
readonly kind: 'SelectQueryNode'
3124
readonly from: FromNode
3225
readonly selections?: ReadonlyArray<SelectionNode>
3326
readonly distinctOnSelections?: ReadonlyArray<SelectionNode>
34-
readonly distinct?: boolean
3527
readonly joins?: ReadonlyArray<JoinNode>
3628
readonly groupBy?: GroupByNode
3729
readonly orderBy?: OrderByNode
3830
readonly where?: WhereNode
39-
readonly modifiers?: ReadonlyArray<SelectModifier>
31+
readonly frontModifiers?: ReadonlyArray<SelectModifierNode>
32+
readonly endModifiers?: ReadonlyArray<SelectModifierNode>
4033
readonly limit?: LimitNode
4134
readonly offset?: OffsetNode
4235
readonly with?: WithNode
@@ -87,14 +80,26 @@ export const SelectQueryNode = freeze({
8780
})
8881
},
8982

90-
cloneWithModifier(
83+
cloneWithFrontModifier(
84+
select: SelectQueryNode,
85+
modifier: SelectModifierNode
86+
): SelectQueryNode {
87+
return freeze({
88+
...select,
89+
frontModifiers: select.frontModifiers
90+
? freeze([...select.frontModifiers, modifier])
91+
: freeze([modifier]),
92+
})
93+
},
94+
95+
cloneWithEndModifier(
9196
select: SelectQueryNode,
92-
modifier: SelectModifier
97+
modifier: SelectModifierNode
9398
): SelectQueryNode {
9499
return freeze({
95100
...select,
96-
modifiers: select.modifiers
97-
? freeze([...select.modifiers, modifier])
101+
endModifiers: select.endModifiers
102+
? freeze([...select.endModifiers, modifier])
98103
: freeze([modifier]),
99104
})
100105
},
@@ -167,13 +172,6 @@ export const SelectQueryNode = freeze({
167172
})
168173
},
169174

170-
cloneWithDistinct(selectNode: SelectQueryNode): SelectQueryNode {
171-
return freeze({
172-
...selectNode,
173-
distinct: true,
174-
})
175-
},
176-
177175
cloneWithUnion(
178176
selectNode: SelectQueryNode,
179177
union: UnionNode

src/query-builder/select-query-builder.ts

Lines changed: 85 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ import { NoResultError, NoResultErrorConstructor } from './no-result-error.js'
5959
import { HavingInterface } from './having-interface.js'
6060
import { IdentifierNode } from '../operation-node/identifier-node.js'
6161
import { AliasedRawBuilder } from '../raw-builder/raw-builder.js'
62+
import { SelectModifierNode } from '../operation-node/select-modifier-node.js'
6263

6364
export class SelectQueryBuilder<DB, TB extends keyof DB, O>
6465
implements
@@ -527,6 +528,68 @@ export class SelectQueryBuilder<DB, TB extends keyof DB, O>
527528
})
528529
}
529530

531+
/**
532+
* This can be used to add any additional SQL to the front of the query __after__ the `select` keyword.
533+
*
534+
* ### Examples
535+
*
536+
* ```ts
537+
* db.selectFrom('person')
538+
* .modifyFront(sql`sql_no_cache`)
539+
* .select('first_name')
540+
* .execute()
541+
* ```
542+
*
543+
* The generated SQL (MySQL):
544+
*
545+
* ```sql
546+
* select sql_no_cache `first_name`
547+
* from `person`
548+
* ```
549+
*/
550+
modifyFront(modifier: AnyRawBuilder): SelectQueryBuilder<DB, TB, O> {
551+
return new SelectQueryBuilder({
552+
...this.#props,
553+
queryNode: SelectQueryNode.cloneWithFrontModifier(
554+
this.#props.queryNode,
555+
SelectModifierNode.createWithRaw(modifier.toOperationNode())
556+
),
557+
})
558+
}
559+
560+
/**
561+
* This can be used to add any additional SQL to the end of the query.
562+
*
563+
* Also see {@link forUpdate}, {@link forShare}, {@link forKeyShare}, {@link forNoKeyUpdate}
564+
* {@link skipLocked} and {@link noWait}.
565+
*
566+
* ### Examples
567+
*
568+
* ```ts
569+
* db.selectFrom('person')
570+
* .select('first_name')
571+
* .modifyEnd(sql`for update`)
572+
* .execute()
573+
* ```
574+
*
575+
* The generated SQL (PostgreSQL):
576+
*
577+
* ```sql
578+
* select "first_name"
579+
* from "person"
580+
* for update
581+
* ```
582+
*/
583+
modifyEnd(modifier: AnyRawBuilder): SelectQueryBuilder<DB, TB, O> {
584+
return new SelectQueryBuilder({
585+
...this.#props,
586+
queryNode: SelectQueryNode.cloneWithEndModifier(
587+
this.#props.queryNode,
588+
SelectModifierNode.createWithRaw(modifier.toOperationNode())
589+
),
590+
})
591+
}
592+
530593
/**
531594
* Makes the selection distinct.
532595
*
@@ -548,84 +611,87 @@ export class SelectQueryBuilder<DB, TB extends keyof DB, O>
548611
distinct(): SelectQueryBuilder<DB, TB, O> {
549612
return new SelectQueryBuilder({
550613
...this.#props,
551-
queryNode: SelectQueryNode.cloneWithDistinct(this.#props.queryNode),
614+
queryNode: SelectQueryNode.cloneWithFrontModifier(
615+
this.#props.queryNode,
616+
SelectModifierNode.create('Distinct')
617+
),
552618
})
553619
}
554620

555621
/**
556-
* Adds the `for update` option to a select query on supported databases.
622+
* Adds the `for update` modifier to a select query on supported databases.
557623
*/
558624
forUpdate(): SelectQueryBuilder<DB, TB, O> {
559625
return new SelectQueryBuilder({
560626
...this.#props,
561-
queryNode: SelectQueryNode.cloneWithModifier(
627+
queryNode: SelectQueryNode.cloneWithEndModifier(
562628
this.#props.queryNode,
563-
'ForUpdate'
629+
SelectModifierNode.create('ForUpdate')
564630
),
565631
})
566632
}
567633

568634
/**
569-
* Adds the `for share` option to a select query on supported databases.
635+
* Adds the `for share` modifier to a select query on supported databases.
570636
*/
571637
forShare(): SelectQueryBuilder<DB, TB, O> {
572638
return new SelectQueryBuilder({
573639
...this.#props,
574-
queryNode: SelectQueryNode.cloneWithModifier(
640+
queryNode: SelectQueryNode.cloneWithEndModifier(
575641
this.#props.queryNode,
576-
'ForShare'
642+
SelectModifierNode.create('ForShare')
577643
),
578644
})
579645
}
580646

581647
/**
582-
* Adds the `for key share` option to a select query on supported databases.
648+
* Adds the `for key share` modifier to a select query on supported databases.
583649
*/
584650
forKeyShare(): SelectQueryBuilder<DB, TB, O> {
585651
return new SelectQueryBuilder({
586652
...this.#props,
587-
queryNode: SelectQueryNode.cloneWithModifier(
653+
queryNode: SelectQueryNode.cloneWithEndModifier(
588654
this.#props.queryNode,
589-
'ForKeyShare'
655+
SelectModifierNode.create('ForKeyShare')
590656
),
591657
})
592658
}
593659

594660
/**
595-
* Adds the `for no key update` option to a select query on supported databases.
661+
* Adds the `for no key update` modifier to a select query on supported databases.
596662
*/
597663
forNoKeyUpdate(): SelectQueryBuilder<DB, TB, O> {
598664
return new SelectQueryBuilder({
599665
...this.#props,
600-
queryNode: SelectQueryNode.cloneWithModifier(
666+
queryNode: SelectQueryNode.cloneWithEndModifier(
601667
this.#props.queryNode,
602-
'ForNoKeyUpdate'
668+
SelectModifierNode.create('ForNoKeyUpdate')
603669
),
604670
})
605671
}
606672

607673
/**
608-
* Adds the `skip locked` option to a select query on supported databases.
674+
* Adds the `skip locked` modifier to a select query on supported databases.
609675
*/
610676
skipLocked(): SelectQueryBuilder<DB, TB, O> {
611677
return new SelectQueryBuilder({
612678
...this.#props,
613-
queryNode: SelectQueryNode.cloneWithModifier(
679+
queryNode: SelectQueryNode.cloneWithEndModifier(
614680
this.#props.queryNode,
615-
'SkipLocked'
681+
SelectModifierNode.create('SkipLocked')
616682
),
617683
})
618684
}
619685

620686
/**
621-
* Adds the `nowait` option to a select query on supported databases.
687+
* Adds the `nowait` modifier to a select query on supported databases.
622688
*/
623689
noWait(): SelectQueryBuilder<DB, TB, O> {
624690
return new SelectQueryBuilder({
625691
...this.#props,
626-
queryNode: SelectQueryNode.cloneWithModifier(
692+
queryNode: SelectQueryNode.cloneWithEndModifier(
627693
this.#props.queryNode,
628-
'NoWait'
694+
SelectModifierNode.create('NoWait')
629695
),
630696
})
631697
}

0 commit comments

Comments
 (0)