@sequeljs/ast
Version:
A SQL AST manager for JavaScript
357 lines (266 loc) • 8.31 kB
text/typescript
import SQLString from '../collectors/SQLString'
import EmptyJoinError from '../errors/EmptyJoinError'
import EngineNotSetError from '../errors/EngineNotSetError'
import VisitorNotSetError from '../errors/VisitorNotSetError'
import Comment from '../nodes/Comment'
import Distinct from '../nodes/Distinct'
import DistinctOn from '../nodes/DistinctOn'
import Except from '../nodes/Except'
import Exists from '../nodes/Exists'
import Group from '../nodes/Group'
import InnerJoin from '../nodes/InnerJoin'
import Intersect from '../nodes/Intersect'
import Join from '../nodes/Join'
import Lateral from '../nodes/Lateral'
import Limit from '../nodes/Limit'
import Lock from '../nodes/Lock'
import NamedWindow from '../nodes/NamedWindow'
import Offset from '../nodes/Offset'
import On from '../nodes/On'
import OptimizerHints from '../nodes/OptimizerHints'
import OuterJoin from '../nodes/OuterJoin'
import SQLLiteral from '../nodes/SQLLiteral'
import SelectStatement from '../nodes/SelectStatement'
import StringJoin from '../nodes/StringJoin'
import Union from '../nodes/Union'
import UnionAll from '../nodes/UnionAll'
import With from '../nodes/With'
import WithRecursive from '../nodes/WithRecursive'
import WhereSQL from '../visitors/WhereSQL'
import SequelAST from '../SequelAST'
import TreeManager from './TreeManager'
import type Engine from '../interfaces/Engine'
import type Relation from '../interfaces/Relation'
import type CRUD from '../mixins/CRUD'
import type SelectCore from '../nodes/SelectCore'
import type TableAlias from '../nodes/TableAlias'
class SelectManager extends TreeManager<SelectManager, SelectStatement> {
protected ctx: SelectCore
get constraints(): SelectCore['wheres'] {
return this.ctx.wheres
}
get froms(): SelectStatement['cores'] {
return this.ast.cores.map((c) => c.from).filter(Boolean)
}
get joinSources(): SelectCore['source']['right'] {
return this.ctx.source.right
}
get limit(): SelectStatement['limit']['expr'] {
return this.ast.limit && this.ast.limit.expr
}
set limit(limit: SelectStatement['limit']['expr']) {
this.take(limit)
}
get locked(): SelectStatement['lock'] {
return this.ast.lock
}
get offset(): SelectStatement['offset']['expr'] {
return this.ast.offset && this.ast.offset.expr
}
set offset(amount: SelectStatement['offset']['expr']) {
this.skip(amount)
}
get orders(): SelectStatement['orders'] {
return this.ast.orders
}
get projections(): SelectCore['projections'] {
return this.ctx.projections
}
set projections(projections: SelectCore['projections']) {
this.ctx.projections = projections
}
get source(): SelectCore['source'] {
return this.ctx.source
}
get taken(): SelectManager['limit'] {
return this.limit
}
constructor(table: any = null) {
super(new SelectStatement())
this.ctx = this.ast.cores[this.ast.cores.length - 1]
this.from(table)
}
private collapse(exprs: any[]): any {
let expressions = exprs
expressions = expressions.filter(Boolean).map((expr) => {
if (typeof expr === 'string') {
return new SQLLiteral(expr)
}
return expr
})
if (expressions.length === 1) {
return expressions[0]
}
return this.createAnd(expressions)
}
as(other: any): TableAlias {
return this.createTableAlias(this.grouping(this.ast), new SQLLiteral(other))
}
comment(...values: any): SelectManager {
this.ctx.comment = new Comment(values)
return this
}
distinct(value = true): SelectManager {
this.ctx.setQuantifier = value ? new Distinct() : null
return this
}
distinctOn(value: any): SelectManager {
this.ctx.setQuantifier = value ? new DistinctOn(value) : null
return this
}
except(other: TreeManager<SelectManager, SelectStatement>): Except {
return new Except(this.ast, other.ast)
}
exists(): Exists {
return new Exists(this.ast)
}
from(table: any): SelectManager {
if (table instanceof Join) {
this.ctx.source.right.push(table)
} else if (typeof table === 'string') {
this.ctx.source.left = new SQLLiteral(table)
} else {
this.ctx.source.left = table
}
return this
}
group(...columns: any[]) {
columns.forEach((column) => {
let col = column
if (typeof col === 'string') {
col = new SQLLiteral(col)
}
this.ctx.groups.push(new Group(col))
})
return this
}
having(expr: any): SelectManager {
this.ctx.havings.push(expr)
return this
}
intersect(other: TreeManager<SelectManager, SelectStatement>): Intersect {
return new Intersect(this.ast, other.ast)
}
join(
relation: string | Relation | SQLLiteral | null,
klass: typeof Join = InnerJoin,
): SelectManager {
if (relation === null) {
return this
}
let Klass: typeof Join = klass
let rel = relation
if (typeof rel === 'string' || rel instanceof SQLLiteral) {
rel = new SQLLiteral(rel)
if (rel.length <= 0) {
throw new EmptyJoinError()
}
Klass = StringJoin
}
this.ctx.source.right.push(this.createJoin(rel, null, Klass))
return this
}
lateral(tableName: string | null = null): Lateral {
const base = tableName ? this.as(tableName) : this.ast
return new Lateral(base)
}
lock(locking: any = new SQLLiteral('FOR UPDATE')): SelectManager {
let lock = locking
if (lock === true) {
lock = new SQLLiteral('FOR UPDATE')
} else if (typeof lock === 'string' || lock instanceof SQLLiteral) {
lock = new SQLLiteral(lock)
}
this.ast.lock = new Lock(lock)
return this
}
minus(other: TreeManager<SelectManager, SelectStatement>): Except {
return this.except(other)
}
on(...exprs: any[]): SelectManager {
this.ctx.source.right[this.ctx.source.right.length - 1].right = new On(
this.collapse(exprs),
)
return this
}
optimizerHints(...hints: any[]): SelectManager {
if (hints.length > 0) {
this.ctx.optimizerHints = new OptimizerHints(hints)
}
return this
}
order(...expr: any[]): SelectManager {
this.ast.orders.push(
...expr.map((e) => (typeof e === 'string' ? new SQLLiteral(e) : e)),
)
return this
}
outerJoin(relation: Relation | null): SelectManager {
return this.join(relation, OuterJoin)
}
project(...projections: any[]): SelectManager {
this.ctx.projections.push(
...projections.map((p) =>
typeof p === 'string' ? new SQLLiteral(p) : p,
),
)
return this
}
skip(amount: any): SelectManager {
this.ast.offset = amount ? new Offset(amount) : null
return this
}
take(limit: any): SelectManager {
if (limit) {
this.ast.limit = new Limit(limit)
} else {
this.ast.limit = null
}
return this
}
union(other: TreeManager<SelectManager, SelectStatement>): Union {
return new Union(this.ast, other.ast)
}
unionAll(other: TreeManager<SelectManager, SelectStatement>): UnionAll {
return new UnionAll(this.ast, other.ast)
}
whereSQL(engine: Engine | null | undefined = undefined): SQLLiteral | null {
let currentEngine = engine
if (typeof currentEngine === 'undefined') {
currentEngine = SequelAST.engine
}
if (!currentEngine) {
throw new EngineNotSetError()
}
if (this.ctx.wheres.length <= 0) {
return null
}
if (!currentEngine.connection.visitor) {
throw new VisitorNotSetError()
}
const visitor = new WhereSQL(
currentEngine.connection.visitor,
currentEngine.connection,
)
return new SQLLiteral(visitor.accept(this.ctx, new SQLString()).value)
}
window(name: any): NamedWindow {
const window = new NamedWindow(name)
this.ctx.windows.push(window)
return window
}
with(...subqueries: any[]): SelectManager {
this.ast.with = new With(
subqueries.reduce((acc, val) => acc.concat(val), []),
)
return this
}
withRecursive(...subqueries: any[]): SelectManager {
this.ast.with = new WithRecursive(
subqueries.reduce((acc, val) => acc.concat(val), []),
)
return this
}
}
interface SelectManager extends CRUD {}
export default SelectManager