Skip to content

Commit 5e7a20d

Browse files
feat: orWhere, Col.cast, TRUNCATE TABLE
- orWhere() on Select/Update/Delete for OR conditions with existing WHERE - Col.cast<R>(dataType) inline CAST shorthand - TRUNCATE TABLE DDL with CASCADE and RESTART IDENTITY - orWhere on all untyped builders too (UpdateBuilder, DeleteBuilder) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent f56c645 commit 5e7a20d

12 files changed

Lines changed: 215 additions & 4 deletions

File tree

AGENTS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,4 +185,4 @@ pnpm release # pnpm test && pnpm build && bumpp && npm publish && git pus
185185
- **No code without tests** — PR must include tests for all new/changed code
186186
- Run all: `pnpm test`
187187
- Run single: `pnpm vitest run test/<path>.test.ts`
188-
- **Current:** 80 test files, 691 tests, 0 lint errors, 0 tsgo errors
188+
- **Current:** 81 test files, 699 tests, 0 lint errors, 0 tsgo errors

src/ast/ddl-nodes.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,15 @@ export interface DropViewNode {
157157
materialized?: boolean
158158
}
159159

160+
// ── TRUNCATE TABLE ──
161+
162+
export interface TruncateTableNode {
163+
type: "truncate_table"
164+
table: TableRefNode
165+
cascade?: boolean
166+
restartIdentity?: boolean
167+
}
168+
160169
// ── Union of all DDL nodes ──
161170

162171
export type DDLNode =
@@ -167,3 +176,4 @@ export type DDLNode =
167176
| DropIndexNode
168177
| CreateViewNode
169178
| DropViewNode
179+
| TruncateTableNode

src/builder/ddl/drop.ts

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
import type { DropIndexNode, DropTableNode, DropViewNode } from "../../ast/ddl-nodes.ts"
1+
import type {
2+
DropIndexNode,
3+
DropTableNode,
4+
DropViewNode,
5+
TruncateTableNode,
6+
} from "../../ast/ddl-nodes.ts"
27
import type { TableRefNode } from "../../ast/nodes.ts"
38

49
// ── DROP TABLE ──
@@ -91,6 +96,34 @@ export class DropViewBuilder {
9196
}
9297
}
9398

99+
// ── TRUNCATE TABLE ──
100+
101+
export class TruncateTableBuilder {
102+
private readonly node: TruncateTableNode
103+
104+
constructor(table: string, schema?: string) {
105+
const ref: TableRefNode = { type: "table_ref", name: table, schema }
106+
this.node = { type: "truncate_table", table: ref }
107+
}
108+
109+
private clone(patch: Partial<TruncateTableNode>): TruncateTableBuilder {
110+
const next = new TruncateTableBuilder(this.node.table.name, this.node.table.schema)
111+
return Object.assign(next, { node: { ...this.node, ...patch } }) as TruncateTableBuilder
112+
}
113+
114+
cascade(): TruncateTableBuilder {
115+
return this.clone({ cascade: true })
116+
}
117+
118+
restartIdentity(): TruncateTableBuilder {
119+
return this.clone({ restartIdentity: true })
120+
}
121+
122+
build(): TruncateTableNode {
123+
return { ...this.node }
124+
}
125+
}
126+
94127
// ── Factory functions ──
95128

96129
export function dropTable(table: string, schema?: string): DropTableBuilder {

src/builder/delete.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,16 @@ export class DeleteBuilder {
3131
return new DeleteBuilder({ ...this.node, where: expr })
3232
}
3333

34+
orWhere(expr: ExpressionNode): DeleteBuilder {
35+
if (this.node.where) {
36+
return new DeleteBuilder({
37+
...this.node,
38+
where: { type: "binary_op", op: "OR", left: this.node.where, right: expr },
39+
})
40+
}
41+
return new DeleteBuilder({ ...this.node, where: expr })
42+
}
43+
3444
/** USING clause (PG: DELETE FROM t USING other WHERE ...) */
3545
using(table: string | TableRefNode): DeleteBuilder {
3646
const ref: TableRefNode = typeof table === "string" ? { type: "table_ref", name: table } : table

src/builder/eb.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,11 @@ export class Col<T> {
254254
return wrap<T>(this._node)
255255
}
256256

257+
/** CAST(col AS dataType) inline */
258+
cast<R>(dataType: string): Expression<R> {
259+
return wrap<R>(rawCast(this._node, dataType))
260+
}
261+
257262
/** ASC ordering — for use with orderBy(col.asc()) */
258263
asc(): { expr: Expression<T>; direction: "ASC" } {
259264
return { expr: wrap<T>(this._node), direction: "ASC" }

src/builder/select.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,16 @@ export class SelectBuilder {
5858
return new SelectBuilder({ ...this.node, where: expr })
5959
}
6060

61+
orWhere(expr: ExpressionNode): SelectBuilder {
62+
if (this.node.where) {
63+
return new SelectBuilder({
64+
...this.node,
65+
where: { type: "binary_op", op: "OR", left: this.node.where, right: expr },
66+
})
67+
}
68+
return new SelectBuilder({ ...this.node, where: expr })
69+
}
70+
6171
join(
6272
type: JoinType,
6373
table: string | TableRefNode,

src/builder/typed-select.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,21 @@ export class TypedSelectBuilder<DB, TB extends keyof DB, O> {
9898
return new TypedSelectBuilder(this._builder.where(unwrap(exprOrCallback)), this._table)
9999
}
100100

101+
/**
102+
* OR WHERE — ORs with existing WHERE clause instead of AND.
103+
*/
104+
orWhere(
105+
exprOrCallback: Expression<boolean> | WhereCallback<DB, TB>,
106+
): TypedSelectBuilder<DB, TB, O> {
107+
if (typeof exprOrCallback === "function") {
108+
resetParams()
109+
const cols = createColumnProxies<DB, TB>(this._table)
110+
const result = exprOrCallback(cols)
111+
return new TypedSelectBuilder(this._builder.orWhere(unwrap(result)), this._table)
112+
}
113+
return new TypedSelectBuilder(this._builder.orWhere(unwrap(exprOrCallback)), this._table)
114+
}
115+
101116
/**
102117
* INNER JOIN.
103118
*

src/builder/update.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,16 @@ export class UpdateBuilder {
3838
return new UpdateBuilder({ ...this.node, where: expr })
3939
}
4040

41+
orWhere(expr: ExpressionNode): UpdateBuilder {
42+
if (this.node.where) {
43+
return new UpdateBuilder({
44+
...this.node,
45+
where: { type: "binary_op", op: "OR", left: this.node.where, right: expr },
46+
})
47+
}
48+
return new UpdateBuilder({ ...this.node, where: expr })
49+
}
50+
4151
from(table: string | TableRefNode): UpdateBuilder {
4252
const ref: TableRefNode = typeof table === "string" ? { type: "table_ref", name: table } : table
4353
return new UpdateBuilder({ ...this.node, from: ref })

src/index.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,12 @@ export {
281281
export { AlterTableBuilder } from "./builder/ddl/alter-table.ts"
282282
export { CreateIndexBuilder } from "./builder/ddl/create-index.ts"
283283
export { CreateViewBuilder } from "./builder/ddl/create-view.ts"
284-
export { DropTableBuilder, DropIndexBuilder, DropViewBuilder } from "./builder/ddl/drop.ts"
284+
export {
285+
DropTableBuilder,
286+
DropIndexBuilder,
287+
DropViewBuilder,
288+
TruncateTableBuilder,
289+
} from "./builder/ddl/drop.ts"
285290
export { DDLPrinter } from "./printer/ddl.ts"
286291
export { SchemaBuilder } from "./sumak.ts"
287292

@@ -303,6 +308,7 @@ export type {
303308
ForeignKeyConstraintNode,
304309
PrimaryKeyConstraintNode,
305310
TableConstraintNode,
311+
TruncateTableNode,
306312
UniqueConstraintNode,
307313
} from "./ast/ddl-nodes.ts"
308314

src/printer/ddl.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import type {
1010
DropViewNode,
1111
ForeignKeyConstraintNode,
1212
TableConstraintNode,
13+
TruncateTableNode,
1314
} from "../ast/ddl-nodes.ts"
1415
import type { CompiledQuery, SQLDialect } from "../types.ts"
1516
import { quoteIdentifier, quoteTableRef } from "../utils/identifier.ts"
@@ -44,6 +45,8 @@ export class DDLPrinter {
4445
return this.printCreateView(node)
4546
case "drop_view":
4647
return this.printDropView(node)
48+
case "truncate_table":
49+
return this.printTruncateTable(node)
4750
}
4851
}
4952

@@ -253,6 +256,14 @@ export class DDLPrinter {
253256
return parts.join(" ")
254257
}
255258

259+
private printTruncateTable(node: TruncateTableNode): string {
260+
const parts: string[] = ["TRUNCATE TABLE"]
261+
parts.push(quoteTableRef(node.table.name, this.dialect, node.table.schema))
262+
if (node.restartIdentity) parts.push("RESTART IDENTITY")
263+
if (node.cascade) parts.push("CASCADE")
264+
return parts.join(" ")
265+
}
266+
256267
private printExpr(node: import("../ast/nodes.ts").ExpressionNode): string {
257268
// Simplified expression printing for DDL contexts
258269
switch (node.type) {

0 commit comments

Comments
 (0)