Skip to content

Commit

Permalink
add support for CREATE TRIGGER
Browse files Browse the repository at this point in the history
  • Loading branch information
thecodrr committed Dec 18, 2023
1 parent 680eb46 commit 5f5b8b8
Show file tree
Hide file tree
Showing 15 changed files with 968 additions and 5 deletions.
2 changes: 1 addition & 1 deletion src/kysely.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ export class Kysely<DB>
/**
* Returns the {@link SchemaModule} module for building database schema.
*/
get schema(): SchemaModule {
get schema(): SchemaModule<DB> {
return new SchemaModule(this.#props.executor)
}

Expand Down
77 changes: 77 additions & 0 deletions src/operation-node/create-trigger-node.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { freeze } from '../util/object-utils.js'
import { OperationNode } from './operation-node.js'
import { TableNode } from './table-node.js'
import { IdentifierNode } from './identifier-node.js'
import { QueryNode } from './query-node.js'
import { TriggerEventNode } from './trigger-event-node.js'
import { TriggerOrderNode } from './trigger-order-node.js'
import { FunctionNode } from './function-node.js'

export type TriggerTime = 'after' | 'before' | 'instead of'

export type CreateTriggerNodeParams = Omit<
CreateTriggerNode,
'kind' | 'name' | 'queries'
>

export interface CreateTriggerNode extends OperationNode {
readonly kind: 'CreateTriggerNode'
readonly name: IdentifierNode
readonly queries?: ReadonlyArray<QueryNode>
readonly function?: FunctionNode
readonly time?: TriggerTime
readonly events?: ReadonlyArray<TriggerEventNode>
readonly table?: TableNode
readonly orReplace?: boolean
readonly ifNotExists?: boolean
readonly when?: OperationNode
readonly temporary?: boolean
readonly forEach?: 'row' | 'statement'
readonly order?: TriggerOrderNode
}

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

create(name: IdentifierNode): CreateTriggerNode {
return freeze({
kind: 'CreateTriggerNode',
name,
})
},

cloneWithQuery(
createTrigger: CreateTriggerNode,
query: QueryNode
): CreateTriggerNode {
return freeze({
...createTrigger,
queries: freeze([...(createTrigger.queries || []), query]),
})
},

cloneWithEvent(
createTrigger: CreateTriggerNode,
event: TriggerEventNode
): CreateTriggerNode {
return freeze({
...createTrigger,
events: freeze([...(createTrigger.events || []), event]),
})
},

cloneWith(
createTrigger: CreateTriggerNode,
params: CreateTriggerNodeParams
): CreateTriggerNode {
return freeze({
...createTrigger,
...params,
})
},
})
40 changes: 40 additions & 0 deletions src/operation-node/drop-trigger-node.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { freeze } from '../util/object-utils.js'
import { OperationNode } from './operation-node.js'
import { SchemableIdentifierNode } from './schemable-identifier-node.js'

export type DropTriggerNodeParams = Omit<
Partial<DropTriggerNode>,
'kind' | 'name'
>
export interface DropTriggerNode extends OperationNode {
readonly kind: 'DropTriggerNode'
readonly name: SchemableIdentifierNode
readonly ifExists?: boolean
readonly cascade?: boolean
}

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

create(name: SchemableIdentifierNode): DropTriggerNode {
return freeze({
kind: 'DropTriggerNode',
name,
})
},

cloneWith(
dropTrigger: DropTriggerNode,
params: DropTriggerNodeParams
): DropTriggerNode {
return freeze({
...dropTrigger,
...params,
})
},
})
51 changes: 51 additions & 0 deletions src/operation-node/operation-node-transformer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ import { JSONPathLegNode } from './json-path-leg-node.js'
import { JSONOperatorChainNode } from './json-operator-chain-node.js'
import { TupleNode } from './tuple-node.js'
import { AddIndexNode } from './add-index-node.js'
import { CreateTriggerNode } from './create-trigger-node.js'
import { DropTriggerNode } from './drop-trigger-node.js'
import { TriggerEventNode } from './trigger-event-node.js'
import { TriggerOrderNode } from './trigger-order-node.js'

/**
* Transforms an operation node tree into another one.
Expand Down Expand Up @@ -210,6 +214,10 @@ export class OperationNodeTransformer {
JSONOperatorChainNode: this.transformJSONOperatorChain.bind(this),
TupleNode: this.transformTuple.bind(this),
AddIndexNode: this.transformAddIndex.bind(this),
CreateTriggerNode: this.transformCreateTrigger.bind(this),
TriggerEventNode: this.transformTriggerEvent.bind(this),
TriggerOrderNode: this.transformTriggerOrder.bind(this),
DropTriggerNode: this.transformDropTrigger.bind(this),
})

transformNode<T extends OperationNode | undefined>(node: T): T {
Expand Down Expand Up @@ -417,6 +425,40 @@ export class OperationNodeTransformer {
})
}

protected transformCreateTrigger(node: CreateTriggerNode): CreateTriggerNode {
return requireAllProps<CreateTriggerNode>({
kind: 'CreateTriggerNode',
name: this.transformNode(node.name),
table: this.transformNode(node.table),
ifNotExists: node.ifNotExists,
time: node.time,
events: this.transformNodeList(node.events),
forEach: node.forEach,
orReplace: node.orReplace,
temporary: node.temporary,
queries: this.transformNodeList(node.queries),
function: this.transformNode(node.function),
when: this.transformNode(node.when),
order: this.transformNode(node.order),
})
}

protected transformTriggerEvent(node: TriggerEventNode): TriggerEventNode {
return requireAllProps<TriggerEventNode>({
kind: 'TriggerEventNode',
event: node.event,
columns: this.transformNodeList(node.columns),
})
}

protected transformTriggerOrder(node: TriggerOrderNode): TriggerOrderNode {
return requireAllProps<TriggerOrderNode>({
kind: 'TriggerOrderNode',
order: node.order,
otherTriggerName: this.transformNode(node.otherTriggerName),
})
}

protected transformColumnDefinition(
node: ColumnDefinitionNode
): ColumnDefinitionNode {
Expand Down Expand Up @@ -455,6 +497,15 @@ export class OperationNodeTransformer {
})
}

protected transformDropTrigger(node: DropTriggerNode): DropTriggerNode {
return requireAllProps<DropTriggerNode>({
kind: 'DropTriggerNode',
name: this.transformNode(node.name),
ifExists: node.ifExists,
cascade: node.cascade,
})
}

protected transformOrderBy(node: OrderByNode): OrderByNode {
return requireAllProps<OrderByNode>({
kind: 'OrderByNode',
Expand Down
12 changes: 12 additions & 0 deletions src/operation-node/operation-node-visitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,10 @@ import { JSONPathLegNode } from './json-path-leg-node.js'
import { JSONOperatorChainNode } from './json-operator-chain-node.js'
import { TupleNode } from './tuple-node.js'
import { AddIndexNode } from './add-index-node.js'
import { CreateTriggerNode } from './create-trigger-node.js'
import { DropTriggerNode } from './drop-trigger-node.js'
import { TriggerEventNode } from './trigger-event-node.js'
import { TriggerOrderNode } from './trigger-order-node.js'

export abstract class OperationNodeVisitor {
protected readonly nodeStack: OperationNode[] = []
Expand Down Expand Up @@ -187,6 +191,10 @@ export abstract class OperationNodeVisitor {
JSONOperatorChainNode: this.visitJSONOperatorChain.bind(this),
TupleNode: this.visitTuple.bind(this),
AddIndexNode: this.visitAddIndex.bind(this),
CreateTriggerNode: this.visitCreateTrigger.bind(this),
TriggerEventNode: this.visitTriggerEvent.bind(this),
TriggerOrderNode: this.visitTriggerOrder.bind(this),
DropTriggerNode: this.visitDropTrigger.bind(this),
})

protected readonly visitNode = (node: OperationNode): void => {
Expand All @@ -213,9 +221,13 @@ export abstract class OperationNodeVisitor {
protected abstract visitDeleteQuery(node: DeleteQueryNode): void
protected abstract visitReturning(node: ReturningNode): void
protected abstract visitCreateTable(node: CreateTableNode): void
protected abstract visitCreateTrigger(node: CreateTriggerNode): void
protected abstract visitTriggerEvent(node: TriggerEventNode): void
protected abstract visitTriggerOrder(node: TriggerOrderNode): void
protected abstract visitAddColumn(node: AddColumnNode): void
protected abstract visitColumnDefinition(node: ColumnDefinitionNode): void
protected abstract visitDropTable(node: DropTableNode): void
protected abstract visitDropTrigger(node: DropTriggerNode): void
protected abstract visitOrderBy(node: OrderByNode): void
protected abstract visitOrderByItem(node: OrderByItemNode): void
protected abstract visitGroupBy(node: GroupByNode): void
Expand Down
4 changes: 4 additions & 0 deletions src/operation-node/operation-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@ export type OperationNodeKind =
| 'JSONOperatorChainNode'
| 'TupleNode'
| 'AddIndexNode'
| 'CreateTriggerNode'
| 'TriggerEventNode'
| 'TriggerOrderNode'
| 'DropTriggerNode'

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

export type TriggerEvent = 'delete' | 'update' | 'insert' | 'truncate'

export type TriggerEventNodeParams = Omit<TriggerEventNode, 'kind'>

export interface TriggerEventNode extends OperationNode {
readonly kind: 'TriggerEventNode'
readonly event: TriggerEvent
readonly columns?: ReadonlyArray<OperationNode>
}

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

create(
event: TriggerEvent,
columns?: ReadonlyArray<OperationNode>
): TriggerEventNode {
return freeze({
kind: 'TriggerEventNode',
event,
columns,
})
},
})
33 changes: 33 additions & 0 deletions src/operation-node/trigger-order-node.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { freeze } from '../util/object-utils.js'
import { IdentifierNode } from './identifier-node.js'
import { OperationNode } from './operation-node.js'

export type TriggerOrder = 'follows' | 'precedes'

export type TriggerOrderNodeParams = Omit<TriggerOrderNode, 'kind'>

export interface TriggerOrderNode extends OperationNode {
readonly kind: 'TriggerOrderNode'
readonly order: TriggerOrder
readonly otherTriggerName: IdentifierNode
}

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

create(
order: TriggerOrder,
otherTriggerName: IdentifierNode
): TriggerOrderNode {
return freeze({
kind: 'TriggerOrderNode',
order,
otherTriggerName,
})
},
})
2 changes: 2 additions & 0 deletions src/plugin/with-schema/with-schema-transformer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,14 @@ const ROOT_OPERATION_NODES: Record<RootOperationNode['kind'], true> = freeze({
CreateTableNode: true,
CreateTypeNode: true,
CreateViewNode: true,
CreateTriggerNode: true,
DeleteQueryNode: true,
DropIndexNode: true,
DropSchemaNode: true,
DropTableNode: true,
DropTypeNode: true,
DropViewNode: true,
DropTriggerNode: true,
InsertQueryNode: true,
RawNode: true,
SelectQueryNode: true,
Expand Down
Loading

0 comments on commit 5f5b8b8

Please sign in to comment.