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 addIndex & dropIndex @ AlterTableBuilder #522

Closed
45 changes: 45 additions & 0 deletions src/operation-node/add-index-node.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { freeze } from '../util/object-utils.js'
import { IdentifierNode } from './identifier-node.js'
import { OperationNode } from './operation-node.js'
import { RawNode } from './raw-node.js'

export type AddIndexNodeProps = Omit<AddIndexNode, 'kind' | 'name'>

export interface AddIndexNode extends OperationNode {
readonly kind: 'AddIndexNode'
readonly name: IdentifierNode
readonly columns?: OperationNode[]
readonly unique?: boolean
readonly using?: RawNode
readonly ifNotExists?: boolean
}

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

create(index: Omit<AddIndexNode, 'kind'>): AddIndexNode {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there ever a situation when it's created with anything but a name at first?
If not, accept a single argument name here.

return freeze({
kind: 'AddIndexNode',
...index,
})
},

cloneWith(node: AddIndexNode, props: AddIndexNodeProps): AddIndexNode {
return freeze({
...node,
...props,
})
},

cloneWithColumns(node: AddIndexNode, columns: OperationNode[]): AddIndexNode {
return freeze({
...node,
columns: [...(node.columns || []), ...columns],
})
},
})
11 changes: 10 additions & 1 deletion src/operation-node/alter-table-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,17 @@ import { AlterColumnNode } from './alter-column-node.js'
import { AddConstraintNode } from './add-constraint-node.js'
import { DropConstraintNode } from './drop-constraint-node.js'
import { ModifyColumnNode } from './modify-column-node.js'
import { DropIndexNode } from './drop-index-node.js'
import { AddIndexNode } from './add-index-node.js'

export type AlterTableNodeTableProps = Pick<
AlterTableNode,
'renameTo' | 'setSchema' | 'addConstraint' | 'dropConstraint'
| 'renameTo'
| 'setSchema'
| 'addConstraint'
| 'dropConstraint'
| 'addIndex'
| 'dropIndex'
>

export type AlterTableColumnAlterationNode =
Expand All @@ -30,6 +37,8 @@ export interface AlterTableNode extends OperationNode {
readonly columnAlterations?: ReadonlyArray<AlterTableColumnAlterationNode>
readonly addConstraint?: AddConstraintNode
readonly dropConstraint?: DropConstraintNode
readonly addIndex?: AddIndexNode
readonly dropIndex?: DropIndexNode
}

/**
Expand Down
15 changes: 15 additions & 0 deletions src/operation-node/operation-node-transformer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ import { UsingNode } from './using-node.js'
import { FunctionNode } from './function-node.js'
import { CaseNode } from './case-node.js'
import { WhenNode } from './when-node.js'
import { AddIndexNode } from './add-index-node.js'

/**
* Transforms an operation node tree into another one.
Expand Down Expand Up @@ -155,6 +156,7 @@ export class OperationNodeTransformer {
OnConflictNode: this.transformOnConflict.bind(this),
OnDuplicateKeyNode: this.transformOnDuplicateKey.bind(this),
CreateIndexNode: this.transformCreateIndex.bind(this),
AddIndexNode: this.transformAddIndex.bind(this),
DropIndexNode: this.transformDropIndex.bind(this),
ListNode: this.transformList.bind(this),
PrimaryKeyConstraintNode: this.transformPrimaryKeyConstraint.bind(this),
Expand Down Expand Up @@ -541,6 +543,17 @@ export class OperationNodeTransformer {
})
}

protected transformAddIndex(node: AddIndexNode): AddIndexNode {
return requireAllProps<AddIndexNode>({
kind: 'AddIndexNode',
name: this.transformNode(node.name),
columns: this.transformNodeList(node.columns),
unique: node.unique,
using: this.transformNode(node.using),
ifNotExists: node.ifNotExists,
})
}

protected transformList(node: ListNode): ListNode {
return requireAllProps<ListNode>({
kind: 'ListNode',
Expand Down Expand Up @@ -681,6 +694,8 @@ export class OperationNodeTransformer {
columnAlterations: this.transformNodeList(node.columnAlterations),
addConstraint: this.transformNode(node.addConstraint),
dropConstraint: this.transformNode(node.dropConstraint),
addIndex: this.transformNode(node.addIndex),
dropIndex: this.transformNode(node.dropIndex),
})
}

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 @@ -84,6 +84,7 @@ import { UsingNode } from './using-node.js'
import { FunctionNode } from './function-node.js'
import { WhenNode } from './when-node.js'
import { CaseNode } from './case-node.js'
import { AddIndexNode } from './add-index-node.js'

export abstract class OperationNodeVisitor {
protected readonly nodeStack: OperationNode[] = []
Expand Down Expand Up @@ -132,6 +133,7 @@ export abstract class OperationNodeVisitor {
OnConflictNode: this.visitOnConflict.bind(this),
OnDuplicateKeyNode: this.visitOnDuplicateKey.bind(this),
CreateIndexNode: this.visitCreateIndex.bind(this),
AddIndexNode: this.visitAddIndex.bind(this),
DropIndexNode: this.visitDropIndex.bind(this),
ListNode: this.visitList.bind(this),
PrimaryKeyConstraintNode: this.visitPrimaryKeyConstraint.bind(this),
Expand Down Expand Up @@ -215,6 +217,7 @@ export abstract class OperationNodeVisitor {
protected abstract visitOnConflict(node: OnConflictNode): void
protected abstract visitOnDuplicateKey(node: OnDuplicateKeyNode): void
protected abstract visitCreateIndex(node: CreateIndexNode): void
protected abstract visitAddIndex(node: AddIndexNode): void
protected abstract visitDropIndex(node: DropIndexNode): void
protected abstract visitList(node: ListNode): void
protected abstract visitPrimaryKeyConstraint(
Expand Down
1 change: 1 addition & 0 deletions src/operation-node/operation-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export type OperationNodeKind =
| 'OnConflictNode'
| 'OnDuplicateKeyNode'
| 'CreateIndexNode'
| 'AddIndexNode'
| 'DropIndexNode'
| 'ListNode'
| 'ReferencesNode'
Expand Down
32 changes: 32 additions & 0 deletions src/query-compiler/default-query-compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ import { UsingNode } from '../operation-node/using-node.js'
import { FunctionNode } from '../operation-node/function-node.js'
import { CaseNode } from '../operation-node/case-node.js'
import { WhenNode } from '../operation-node/when-node.js'
import { AddIndexNode } from '../operation-node/add-index-node.js'

export class DefaultQueryCompiler
extends OperationNodeVisitor
Expand Down Expand Up @@ -806,6 +807,29 @@ export class DefaultQueryCompiler
}
}

protected override visitAddIndex(node: AddIndexNode): void {
this.append('add ')

if (node.unique) {
this.append('unique ')
}

this.append('index ')

this.visitNode(node.name)

if (node.columns) {
this.append(' (')
this.compileList(node.columns)
this.append(')')
}

if (node.using) {
this.append(' using ')
this.visitNode(node.using)
}
}

protected override visitDropIndex(node: DropIndexNode): void {
this.append('drop index ')

Expand Down Expand Up @@ -972,6 +996,14 @@ export class DefaultQueryCompiler
if (node.columnAlterations) {
this.compileList(node.columnAlterations)
}

if (node.addIndex) {
this.visitNode(node.addIndex)
}

if (node.dropIndex) {
this.visitNode(node.dropIndex)
}
}

protected override visitAddColumn(node: AddColumnNode): void {
Expand Down
104 changes: 104 additions & 0 deletions src/schema/add-index-builder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import { Expression } from '../expression/expression.js'
import { AddIndexNode } from '../operation-node/add-index-node.js'
import { IndexType } from '../operation-node/create-index-node.js'
import { OperationNodeSource } from '../operation-node/operation-node-source.js'
import { RawNode } from '../operation-node/raw-node.js'
import {
ExtractColumnNameFromOrderedColumnName,
OrderedColumnName,
parseOrderedColumnName,
} from '../parser/reference-parser.js'
import { QueryExecutor } from '../query-executor/query-executor.js'
import { freeze } from '../util/object-utils.js'
import { preventAwait } from '../util/prevent-await.js'
import { QueryId } from '../util/query-id.js'

export interface AddIndexBuilderInterface<R> {
unique(): R
columns<CL extends string>(columns: OrderedColumnName<CL>[]): R
expression(expression: Expression<any>): R
using(indexType: IndexType): R
using(indexType: string): R
}

export class AddIndexBuilder<C = never>
implements AddIndexBuilderInterface<AddIndexBuilder>, OperationNodeSource
{
readonly #props: AddIndexBuilderProps

constructor(props: AddIndexBuilderProps) {
this.#props = freeze(props)
}

unique(): AddIndexBuilder<C> {
return new AddIndexBuilder({
...this.#props,
node: AddIndexNode.cloneWith(this.#props.node, {
unique: true,
}),
})
}

column<CL extends string>(
column: OrderedColumnName<CL>
): AddIndexBuilder<C | ExtractColumnNameFromOrderedColumnName<CL>> {
return new AddIndexBuilder({
...this.#props,
node: AddIndexNode.cloneWithColumns(this.#props.node, [
parseOrderedColumnName(column),
]),
})
}

columns<CL extends string>(
columns: OrderedColumnName<CL>[]
): AddIndexBuilder<C | ExtractColumnNameFromOrderedColumnName<CL>> {
return new AddIndexBuilder({
...this.#props,
node: AddIndexNode.cloneWithColumns(
this.#props.node,
columns.map(parseOrderedColumnName)
),
})
}

expression(expression: Expression<any>): AddIndexBuilder<C> {
return new AddIndexBuilder({
...this.#props,
node: AddIndexNode.cloneWithColumns(this.#props.node, [
expression.toOperationNode(),
]),
})
}

using(indexType: IndexType): AddIndexBuilder<C>
using(indexType: string): AddIndexBuilder<C>
using(indexType: string): AddIndexBuilder<C> {
return new AddIndexBuilder({
...this.#props,
node: AddIndexNode.cloneWith(this.#props.node, {
using: RawNode.createWithSql(indexType),
}),
})
}

/**
* Simply calls the provided function passing `this` as the only argument. `$call` returns
* what the provided function returns.
*/
$call<T>(func: (qb: this) => T): T {
return func(this)
}

toOperationNode(): AddIndexNode {
return this.#props.node
}
}

export interface AddIndexBuilderProps {
readonly queryId: QueryId
readonly executor: QueryExecutor
readonly node: AddIndexNode
}

preventAwait(AddIndexBuilder, "don't await AddIndexBuilder instances directly.")
Loading