Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add OUTPUT clause support. #828

Merged
merged 42 commits into from
Mar 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
a5e9767
OutputNode.
igalklebanov Dec 31, 2023
33e9884
add to insert query compilation.
igalklebanov Dec 31, 2023
87a1ad2
QueryNode.cloneWithOutput.
igalklebanov Dec 31, 2023
ec31f2c
OutputInterface with output method only.
igalklebanov Dec 31, 2023
5839b6f
enable returning @ mssql adapter.
igalklebanov Dec 31, 2023
575e3ea
add output to insert query builder.
igalklebanov Dec 31, 2023
f7b2996
add outputAll.
igalklebanov Jan 1, 2024
aae6e70
add to delete query compilation.
igalklebanov Jan 1, 2024
a27ee6b
add to delete query builder.
igalklebanov Jan 1, 2024
c493875
add to update query compilation.
igalklebanov Jan 1, 2024
9700448
add to update query builder.
igalklebanov Jan 1, 2024
96f511b
Merge branch 'master' into feat-output
igalklebanov Jan 1, 2024
17122ec
jsdocs.
igalklebanov Jan 1, 2024
29605c6
supportsOutput.
igalklebanov Jan 1, 2024
f8a2a07
supportsOutput.
igalklebanov Jan 1, 2024
b6f158b
use supportsOutput @ insert query builder.
igalklebanov Jan 1, 2024
6198f1e
use supportsOutput @ delete query builder.
igalklebanov Jan 1, 2024
b4e8184
use supportsOutput @ update query builder.
igalklebanov Jan 1, 2024
9f1b38c
compilation fixes.
igalklebanov Jan 1, 2024
909450c
export output interface.
igalklebanov Jan 1, 2024
f0395da
insert test cases.
igalklebanov Jan 1, 2024
18152ba
delete test cases.
igalklebanov Jan 1, 2024
7d83363
update test cases.
igalklebanov Jan 1, 2024
ffd1634
typings tests.
igalklebanov Jan 1, 2024
29fbd23
Merge branch 'master' into feat-output
igalklebanov Jan 8, 2024
a454240
Merge branch 'master' into feat-output
igalklebanov Feb 14, 2024
fcfab01
Merge branch 'master' into feat-output
igalklebanov Feb 14, 2024
cfbffaf
Merge branch 'master' into feat-output
igalklebanov Feb 24, 2024
dba82b6
Merge branch 'master' into feat-output
igalklebanov Feb 24, 2024
fcda024
re-add test cases.
igalklebanov Feb 25, 2024
e672e89
Merge branch 'master' into feat-output
igalklebanov Mar 23, 2024
c3c86ca
make supportsOutput optional.
igalklebanov Mar 23, 2024
83fbb3a
remove unused imports.
igalklebanov Mar 23, 2024
f897cdc
add merge example to output jsdocs.
igalklebanov Mar 23, 2024
4b649d7
handle merge output compilation.
igalklebanov Mar 23, 2024
3686503
add output @ merge query builder.
igalklebanov Mar 23, 2024
7559942
transformer.
igalklebanov Mar 23, 2024
a72a553
leftovers merge query builder.
igalklebanov Mar 23, 2024
037a787
some merge tests.
igalklebanov Mar 23, 2024
b846468
merge typings tests.
igalklebanov Mar 23, 2024
39da41e
Merge branch 'master' into feat-output
igalklebanov Mar 23, 2024
8ea1e49
fix returning types.
igalklebanov Mar 23, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/dialect/dialect-adapter-base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ export abstract class DialectAdapterBase implements DialectAdapter {
return false
}

get supportsOutput(): boolean {
return false
}

abstract acquireMigrationLock(
db: Kysely<any>,
options: MigrationLockOptions,
Expand Down
6 changes: 6 additions & 0 deletions src/dialect/dialect-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ export interface DialectAdapter {
*/
readonly supportsReturning: boolean

/**
* Whether or not this dialect supports the `output` clause in inserts
* updates and deletes.
*/
readonly supportsOutput?: boolean

/**
* This method is used to acquire a lock for the migrations so that
* it's not possible for two migration operations to run in parallel.
Expand Down
6 changes: 2 additions & 4 deletions src/dialect/mssql/mssql-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,8 @@ export class MssqlAdapter extends DialectAdapterBase {
return true
}

get supportsReturning(): boolean {
// mssql should support returning with the `output` clause.
// we need to figure this out when we'll introduce support for it.
return false
get supportsOutput(): boolean {
return true
}

async acquireMigrationLock(db: Kysely<any>): Promise<void> {
Expand Down
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export * from './expression/expression-wrapper.js'

export * from './query-builder/where-interface.js'
export * from './query-builder/returning-interface.js'
export * from './query-builder/output-interface.js'
export * from './query-builder/having-interface.js'
export * from './query-builder/select-query-builder.js'
export * from './query-builder/insert-query-builder.js'
Expand Down Expand Up @@ -203,6 +204,7 @@ export * from './operation-node/merge-query-node.js'
export * from './operation-node/matched-node.js'
export * from './operation-node/fetch-node.js'
export * from './operation-node/top-node.js'
export * from './operation-node/output-node.js'

export * from './util/column-type.js'
export * from './util/compilable.js'
Expand Down
2 changes: 2 additions & 0 deletions src/operation-node/delete-query-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { OrderByItemNode } from './order-by-item-node.js'
import { ExplainNode } from './explain-node.js'
import { UsingNode } from './using-node.js'
import { TopNode } from './top-node.js'
import { OutputNode } from './output-node.js'

export interface DeleteQueryNode extends OperationNode {
readonly kind: 'DeleteQueryNode'
Expand All @@ -24,6 +25,7 @@ export interface DeleteQueryNode extends OperationNode {
readonly limit?: LimitNode
readonly explain?: ExplainNode
readonly top?: TopNode
readonly output?: OutputNode
}

/**
Expand Down
2 changes: 2 additions & 0 deletions src/operation-node/insert-query-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { ExplainNode } from './explain-node.js'
import { OnConflictNode } from './on-conflict-node.js'
import { OnDuplicateKeyNode } from './on-duplicate-key-node.js'
import { OperationNode } from './operation-node.js'
import { OutputNode } from './output-node.js'
import { ReturningNode } from './returning-node.js'
import { TableNode } from './table-node.js'
import { TopNode } from './top-node.js'
Expand All @@ -25,6 +26,7 @@ export interface InsertQueryNode extends OperationNode {
readonly explain?: ExplainNode
readonly defaultValues?: boolean
readonly top?: TopNode
readonly output?: OutputNode
}

/**
Expand Down
2 changes: 2 additions & 0 deletions src/operation-node/merge-query-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { freeze } from '../util/object-utils.js'
import { AliasNode } from './alias-node.js'
import { JoinNode } from './join-node.js'
import { OperationNode } from './operation-node.js'
import { OutputNode } from './output-node.js'
import { TableNode } from './table-node.js'
import { TopNode } from './top-node.js'
import { WhenNode } from './when-node.js'
Expand All @@ -14,6 +15,7 @@ export interface MergeQueryNode extends OperationNode {
readonly whens?: ReadonlyArray<WhenNode>
readonly with?: WithNode
readonly top?: TopNode
readonly output?: OutputNode
}

/**
Expand Down
13 changes: 13 additions & 0 deletions src/operation-node/operation-node-transformer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ import { AddIndexNode } from './add-index-node.js'
import { CastNode } from './cast-node.js'
import { FetchNode } from './fetch-node.js'
import { TopNode } from './top-node.js'
import { OutputNode } from './output-node.js'

/**
* Transforms an operation node tree into another one.
Expand Down Expand Up @@ -220,6 +221,7 @@ export class OperationNodeTransformer {
CastNode: this.transformCast.bind(this),
FetchNode: this.transformFetch.bind(this),
TopNode: this.transformTop.bind(this),
OutputNode: this.transformOutput.bind(this),
})

transformNode<T extends OperationNode | undefined>(node: T): T {
Expand Down Expand Up @@ -384,6 +386,7 @@ export class OperationNodeTransformer {
explain: this.transformNode(node.explain),
defaultValues: node.defaultValues,
top: this.transformNode(node.top),
output: this.transformNode(node.output),
})
}

Expand All @@ -407,6 +410,7 @@ export class OperationNodeTransformer {
limit: this.transformNode(node.limit),
explain: this.transformNode(node.explain),
top: this.transformNode(node.top),
output: this.transformNode(node.output),
})
}

Expand Down Expand Up @@ -514,6 +518,7 @@ export class OperationNodeTransformer {
explain: this.transformNode(node.explain),
limit: this.transformNode(node.limit),
top: this.transformNode(node.top),
output: this.transformNode(node.output),
})
}

Expand Down Expand Up @@ -1008,6 +1013,7 @@ export class OperationNodeTransformer {
whens: this.transformNodeList(node.whens),
with: this.transformNode(node.with),
top: this.transformNode(node.top),
output: this.transformNode(node.output),
})
}

Expand Down Expand Up @@ -1054,6 +1060,13 @@ export class OperationNodeTransformer {
})
}

protected transformOutput(node: OutputNode): OutputNode {
return requireAllProps<OutputNode>({
kind: 'OutputNode',
selections: this.transformNodeList(node.selections),
})
}

protected transformDataType(node: DataTypeNode): DataTypeNode {
// An Object.freezed leaf node. No need to clone.
return node
Expand Down
3 changes: 3 additions & 0 deletions src/operation-node/operation-node-visitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ import { AddIndexNode } from './add-index-node.js'
import { CastNode } from './cast-node.js'
import { FetchNode } from './fetch-node.js'
import { TopNode } from './top-node.js'
import { OutputNode } from './output-node.js'

export abstract class OperationNodeVisitor {
protected readonly nodeStack: OperationNode[] = []
Expand Down Expand Up @@ -197,6 +198,7 @@ export abstract class OperationNodeVisitor {
CastNode: this.visitCast.bind(this),
FetchNode: this.visitFetch.bind(this),
TopNode: this.visitTop.bind(this),
OutputNode: this.visitOutput.bind(this),
})

protected readonly visitNode = (node: OperationNode): void => {
Expand Down Expand Up @@ -307,4 +309,5 @@ export abstract class OperationNodeVisitor {
protected abstract visitCast(node: CastNode): void
protected abstract visitFetch(node: FetchNode): void
protected abstract visitTop(node: TopNode): void
protected abstract visitOutput(node: OutputNode): void
}
1 change: 1 addition & 0 deletions src/operation-node/operation-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ export type OperationNodeKind =
| 'CastNode'
| 'FetchNode'
| 'TopNode'
| 'OutputNode'

export interface OperationNode {
readonly kind: OperationNodeKind
Expand Down
35 changes: 35 additions & 0 deletions src/operation-node/output-node.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { freeze } from '../util/object-utils.js'
import { OperationNode } from './operation-node.js'

export interface OutputNode extends OperationNode {
readonly kind: 'OutputNode'
readonly selections: ReadonlyArray<OperationNode>
}

/**
* @internal
*/
export const OutputNode = freeze({
is(node: OperationNode): node is OutputNode {
return node.kind === 'OutputNode'
},

create(selections: ReadonlyArray<OperationNode>): OutputNode {
return freeze({
kind: 'OutputNode',
selections: freeze(selections),
})
},

cloneWithSelections(
output: OutputNode,
selections: ReadonlyArray<OperationNode>
): OutputNode {
return freeze({
...output,
selections: output.selections
? freeze([...output.selections, ...selections])
: freeze(selections),
})
},
})
14 changes: 14 additions & 0 deletions src/operation-node/query-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { ExplainFormat } from '../util/explainable.js'
import { Expression } from '../expression/expression.js'
import { MergeQueryNode } from './merge-query-node.js'
import { TopNode } from './top-node.js'
import { OutputNode } from './output-node.js'

export type QueryNode =
| SelectQueryNode
Expand All @@ -26,6 +27,7 @@ type HasWhere = { where?: WhereNode }
type HasReturning = { returning?: ReturningNode }
type HasExplain = { explain?: ExplainNode }
type HasTop = { top?: TopNode }
type HasOutput = { output?: OutputNode }

/**
* @internal
Expand Down Expand Up @@ -100,4 +102,16 @@ export const QueryNode = freeze({
top,
})
},

cloneWithOutput<T extends HasOutput>(
node: T,
selections: ReadonlyArray<SelectionNode>,
): T {
return freeze({
...node,
output: node.output
? OutputNode.cloneWithSelections(node.output, selections)
: OutputNode.create(selections),
})
},
})
2 changes: 2 additions & 0 deletions src/operation-node/update-query-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { FromNode } from './from-node.js'
import { ExplainNode } from './explain-node.js'
import { LimitNode } from './limit-node.js'
import { TopNode } from './top-node.js'
import { OutputNode } from './output-node.js'

export type UpdateValuesNode = ValueListNode | PrimitiveValueListNode

Expand All @@ -26,6 +27,7 @@ export interface UpdateQueryNode extends OperationNode {
readonly explain?: ExplainNode
readonly limit?: LimitNode
readonly top?: TopNode
readonly output?: OutputNode
}

/**
Expand Down
47 changes: 19 additions & 28 deletions src/parser/returning-parser.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,29 @@
import { DeleteResult } from '../query-builder/delete-result.js'
import { InsertResult } from '../query-builder/insert-result.js'
import { MergeResult } from '../query-builder/merge-result.js'
import { UpdateResult } from '../query-builder/update-result.js'
import { Selection, AllSelection, CallbackSelection } from './select-parser.js'

export type ReturningRow<
DB,
TB extends keyof DB,
O,
SE,
> = O extends InsertResult
export type ReturningRow<DB, TB extends keyof DB, O, SE> = O extends
| InsertResult
| DeleteResult
| UpdateResult
| MergeResult
? Selection<DB, TB, SE>
: O extends DeleteResult
? Selection<DB, TB, SE>
: O extends UpdateResult
? Selection<DB, TB, SE>
: O & Selection<DB, TB, SE>
: O & Selection<DB, TB, SE>

export type ReturningCallbackRow<
DB,
TB extends keyof DB,
O,
CB,
> = O extends InsertResult
export type ReturningCallbackRow<DB, TB extends keyof DB, O, CB> = O extends
| InsertResult
| DeleteResult
| UpdateResult
| MergeResult
? CallbackSelection<DB, TB, CB>
: O extends DeleteResult
? CallbackSelection<DB, TB, CB>
: O extends UpdateResult
? CallbackSelection<DB, TB, CB>
: O & CallbackSelection<DB, TB, CB>
: O & CallbackSelection<DB, TB, CB>

export type ReturningAllRow<DB, TB extends keyof DB, O> = O extends InsertResult
export type ReturningAllRow<DB, TB extends keyof DB, O> = O extends
| InsertResult
| DeleteResult
| UpdateResult
| MergeResult
? AllSelection<DB, TB>
: O extends DeleteResult
? AllSelection<DB, TB>
: O extends UpdateResult
? AllSelection<DB, TB>
: O & AllSelection<DB, TB>
: O & AllSelection<DB, TB>
Loading
Loading