Import other modules into monorepo
Some checks failed
continuous-integration/drone/push Build is failing
Some checks failed
continuous-integration/drone/push Build is failing
This commit is contained in:
574
src/orm/builder/AbstractBuilder.ts
Normal file
574
src/orm/builder/AbstractBuilder.ts
Normal file
@@ -0,0 +1,574 @@
|
||||
import {Inject} from "../../di";
|
||||
import {DatabaseService} from "../DatabaseService";
|
||||
import {
|
||||
Constraint, ConstraintConnectionOperator,
|
||||
ConstraintOperator,
|
||||
OrderDirection,
|
||||
OrderStatement, QueryResult,
|
||||
QuerySource,
|
||||
SpecifiedField
|
||||
} from "../types";
|
||||
import {Connection} from "../connection/Connection";
|
||||
import {deepCopy, ErrorWithContext} from "../../util";
|
||||
import {EscapeValue, QuerySafeValue, raw} from "../dialect/SQLDialect";
|
||||
import {ResultCollection} from "./result/ResultCollection";
|
||||
import {AbstractResultIterable} from "./result/AbstractResultIterable";
|
||||
import {AppClass} from "../../lifecycle/AppClass";
|
||||
|
||||
/**
|
||||
* Type alias for a function that applies some constraints to a builder group.
|
||||
*/
|
||||
export type ConstraintGroupClosure<T> = (group: AbstractBuilder<T>) => any
|
||||
|
||||
/**
|
||||
* A base class that facilitates building database queries using a fluent interface.
|
||||
* This can be specialized by child-classes to yield query results of the given type `T`.
|
||||
*/
|
||||
export abstract class AbstractBuilder<T> extends AppClass {
|
||||
@Inject()
|
||||
protected readonly databaseService!: DatabaseService
|
||||
|
||||
/** Constraints applied to this query. */
|
||||
protected constraints: Constraint[] = []
|
||||
|
||||
/** The source table to query from. */
|
||||
protected source?: QuerySource
|
||||
|
||||
/** The fields to query from the table. */
|
||||
protected _fields: SpecifiedField[] = []
|
||||
|
||||
/** The number of records to skip before the result set. */
|
||||
protected _skip?: number
|
||||
|
||||
/** The max number of records to include in the result set. */
|
||||
protected _take?: number
|
||||
|
||||
/** If true, the query should refer to distinct records. */
|
||||
protected _distinct: boolean = false
|
||||
|
||||
/** Array of SQL group-by clauses. */
|
||||
protected _groupings: string[] = []
|
||||
|
||||
/** Array of SQL order-by clauses. */
|
||||
protected _orders: OrderStatement[] = []
|
||||
|
||||
/** The connection on which the query should be executed. */
|
||||
protected _connection?: Connection
|
||||
|
||||
/**
|
||||
* Create a new, empty, instance of the current builder.
|
||||
*/
|
||||
public abstract getNewInstance(): AbstractBuilder<T>
|
||||
|
||||
/**
|
||||
* Get a result iterable for the built query.
|
||||
*/
|
||||
public abstract getResultIterable(): AbstractResultIterable<T>
|
||||
|
||||
/**
|
||||
* Clone the current query to a new AbstractBuilder instance with the same properties.
|
||||
*/
|
||||
public clone(): AbstractBuilder<T> {
|
||||
const bldr = this.getNewInstance()
|
||||
|
||||
bldr.constraints = deepCopy(this.constraints)
|
||||
bldr.source = deepCopy(this.source)
|
||||
bldr._fields = deepCopy(this._fields)
|
||||
bldr._skip = deepCopy(this._skip)
|
||||
bldr._take = deepCopy(this._take)
|
||||
bldr._distinct = deepCopy(this._distinct)
|
||||
bldr._groupings = deepCopy(this._groupings)
|
||||
bldr._orders = deepCopy(this._orders)
|
||||
bldr._connection = this._connection
|
||||
|
||||
return bldr
|
||||
}
|
||||
|
||||
/** Get the constraints applied to this query. */
|
||||
public get appliedConstraints() {
|
||||
return deepCopy(this.constraints)
|
||||
}
|
||||
|
||||
/** Get the fields that should be included in this query. */
|
||||
public get appliedFields() {
|
||||
return deepCopy(this._fields)
|
||||
}
|
||||
|
||||
/** Get the skip/take values of this query. */
|
||||
public get appliedPagination() {
|
||||
return { skip: this._skip, take: this._take }
|
||||
}
|
||||
|
||||
/** True if the query should be DISTINCT */
|
||||
public get appliedDistinction() {
|
||||
return this._distinct
|
||||
}
|
||||
|
||||
/** Get the SQL group-by clauses applied to this query. */
|
||||
public get appliedGroupings() {
|
||||
return deepCopy(this._groupings)
|
||||
}
|
||||
|
||||
/** Get the SQL order-by clauses applied to this query. */
|
||||
public get appliedOrder() {
|
||||
return deepCopy(this._orders)
|
||||
}
|
||||
|
||||
/** Get the source table for this query. */
|
||||
public get querySource() {
|
||||
if ( this.source ) return deepCopy(this.source)
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the source table (and optional alias) for this query.
|
||||
* @param table
|
||||
* @param alias
|
||||
*/
|
||||
from(table: string, alias?: string) {
|
||||
if ( alias ) {
|
||||
this.source = { table, alias }
|
||||
} else {
|
||||
this.source = table
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias of `from()`.
|
||||
* @param table
|
||||
* @param alias
|
||||
*/
|
||||
table(table: string, alias?: string) {
|
||||
return this.from(table, alias)
|
||||
}
|
||||
|
||||
/**
|
||||
* Include the given field (and optional alias) in the query.
|
||||
* @param field
|
||||
* @param alias
|
||||
*/
|
||||
field(field: string | QuerySafeValue, alias?: string) {
|
||||
if ( alias ) {
|
||||
this._fields.push({ field, alias })
|
||||
} else {
|
||||
this._fields.push(field)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Include the given fields in the query.
|
||||
* @param fields
|
||||
*/
|
||||
fields(...fields: SpecifiedField[]): this {
|
||||
this._fields = [...this._fields, ...fields]
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias of `fields()`.
|
||||
* @param fields
|
||||
*/
|
||||
returning(...fields: SpecifiedField[]): this {
|
||||
return this.fields(...fields)
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias of `fields()`.
|
||||
* @param fields
|
||||
*/
|
||||
select(...fields: SpecifiedField[]): this {
|
||||
return this.fields(...fields)
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all selected fields from this query.
|
||||
*/
|
||||
clearFields() {
|
||||
this._fields = []
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a new WHERE constraint to the query.
|
||||
* @param field
|
||||
* @param operator
|
||||
* @param operand
|
||||
*/
|
||||
where(field: string | ConstraintGroupClosure<T>, operator?: ConstraintOperator, operand?: EscapeValue) {
|
||||
this.createConstraint('AND', field, operator, operand)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a new WHERE constraint to the query, without escaping `operand`. Prefer `where()`.
|
||||
* @param field
|
||||
* @param operator
|
||||
* @param operand
|
||||
*/
|
||||
whereRaw(field: string, operator: ConstraintOperator, operand: string) {
|
||||
this.createConstraint('AND', field, operator, raw(operand))
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a new WHERE NOT constraint to the query.
|
||||
* @param field
|
||||
* @param operator
|
||||
* @param operand
|
||||
*/
|
||||
whereNot(field: string | ConstraintGroupClosure<T>, operator?: ConstraintOperator, operand?: EscapeValue) {
|
||||
this.createConstraint('AND NOT', field, operator, operand)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply an OR WHERE constraint to the query.
|
||||
* @param field
|
||||
* @param operator
|
||||
* @param operand
|
||||
*/
|
||||
orWhere(field: string | ConstraintGroupClosure<T>, operator?: ConstraintOperator, operand?: EscapeValue) {
|
||||
this.createConstraint('OR', field, operator, operand)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply an OR WHERE NOT constraint to the query.
|
||||
* @param field
|
||||
* @param operator
|
||||
* @param operand
|
||||
*/
|
||||
orWhereNot(field: string | ConstraintGroupClosure<T>, operator?: ConstraintOperator, operand?: EscapeValue) {
|
||||
this.createConstraint('OR NOT', field, operator, operand)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply an OR WHERE constraint to the query, without escaping `operand`. Prefer `orWhere()`.
|
||||
* @param field
|
||||
* @param operator
|
||||
* @param operand
|
||||
*/
|
||||
orWhereRaw(field: string, operator: ConstraintOperator, operand: string) {
|
||||
this.createConstraint('OR', field, operator, raw(operand))
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a WHERE IN constraint to the query, escaping the values in the set.
|
||||
* @param field
|
||||
* @param values
|
||||
*/
|
||||
whereIn(field: string, values: EscapeValue) {
|
||||
this.constraints.push({
|
||||
field,
|
||||
operator: 'IN',
|
||||
operand: values,
|
||||
preop: 'AND',
|
||||
})
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a WHERE NOT IN constraint to the query, escaping the values in the set.
|
||||
* @param field
|
||||
* @param values
|
||||
*/
|
||||
whereNotIn(field: string, values: EscapeValue) {
|
||||
this.constraints.push({
|
||||
field,
|
||||
operator: 'NOT IN',
|
||||
operand: values,
|
||||
preop: 'AND',
|
||||
})
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply an OR WHERE IN constraint to the query, escaping the values in the set.
|
||||
* @param field
|
||||
* @param values
|
||||
*/
|
||||
orWhereIn(field: string, values: EscapeValue) {
|
||||
this.constraints.push({
|
||||
field,
|
||||
operator: 'IN',
|
||||
operand: values,
|
||||
preop: 'OR'
|
||||
})
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply an OR WHERE NOT IN constraint to the query, escaping the values in the set.
|
||||
* @param field
|
||||
* @param values
|
||||
*/
|
||||
orWhereNotIn(field: string, values: EscapeValue) {
|
||||
this.constraints.push({
|
||||
field,
|
||||
operator: 'NOT IN',
|
||||
operand: values,
|
||||
preop: 'OR'
|
||||
})
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Limit the query to a maximum number of rows.
|
||||
* @param rows
|
||||
*/
|
||||
limit(rows: number) {
|
||||
this._take = rows
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias of `limit()`.
|
||||
* @param rows
|
||||
*/
|
||||
take(rows: number) {
|
||||
return this.limit(rows)
|
||||
}
|
||||
|
||||
/**
|
||||
* Skip the first `rows` many rows in the result set.
|
||||
* @param rows
|
||||
*/
|
||||
skip(rows: number) {
|
||||
this._skip = rows
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias of `skip()`.
|
||||
* @param rows
|
||||
*/
|
||||
offset(rows: number) {
|
||||
return this.skip(rows)
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the query return only distinct rows.
|
||||
*/
|
||||
distinct() {
|
||||
this._distinct = true
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow the query to return non-distinct rows. (Undoes `distinct()`.)
|
||||
*/
|
||||
notDistinct() {
|
||||
this._distinct = false
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply `skip()` and `take()` calls to retrieve the records that should appear on
|
||||
* the `pageNum` page, assuming each page has `pageSize` many records.
|
||||
* @param pageNum
|
||||
* @param pageSize
|
||||
*/
|
||||
page(pageNum: number = 1, pageSize: number = 20) {
|
||||
this.skip(pageSize * (pageNum - 1))
|
||||
this.take(pageSize)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply one or more GROUP-BY clauses to the query.
|
||||
* @param groupings
|
||||
*/
|
||||
groupBy(...groupings: string[]) {
|
||||
this._groupings = groupings
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Order the query by the given field.
|
||||
* @param field
|
||||
* @param direction
|
||||
*/
|
||||
orderBy(field: string, direction: OrderDirection = 'ASC') {
|
||||
this._orders.push({ field, direction })
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Order the query by the given field, ascending.
|
||||
* @param field
|
||||
*/
|
||||
orderByAscending(field: string) {
|
||||
return this.orderBy(field, 'ASC')
|
||||
}
|
||||
|
||||
/**
|
||||
* Order the query by the given field, descending.
|
||||
* @param field
|
||||
*/
|
||||
orderByDescending(field: string) {
|
||||
return this.orderBy(field, 'DESC')
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the connection name or instance to execute the query on.
|
||||
* @param nameOrInstance
|
||||
*/
|
||||
connection(nameOrInstance: string | Connection) {
|
||||
if ( nameOrInstance instanceof Connection ) {
|
||||
this._connection = nameOrInstance
|
||||
} else {
|
||||
this._connection = this.databaseService.get(nameOrInstance)
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a result iterable for the rows of this query.
|
||||
*/
|
||||
iterator(): AbstractResultIterable<T> {
|
||||
if ( !this._connection ) {
|
||||
throw new ErrorWithContext(`No connection specified to fetch iterator for query.`)
|
||||
}
|
||||
|
||||
return this.getResultIterable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an async collection of the rows resulting from this query.
|
||||
*/
|
||||
get(): ResultCollection<T> {
|
||||
return new ResultCollection<T>(this.iterator())
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the first record matched by this query, if it exists.
|
||||
*/
|
||||
async first(): Promise<T | undefined> {
|
||||
return this.iterator().at(0)
|
||||
}
|
||||
|
||||
/**
|
||||
* Run an UPDATE query for all rows matched by this query, setting the given data.
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* query.table('my_table').update({ my_col: 4 })
|
||||
* ```
|
||||
*
|
||||
* This is equivalent to:
|
||||
* ```sql
|
||||
* UPDATE TO my_table
|
||||
* SET
|
||||
* my_col = 4
|
||||
* ```
|
||||
*
|
||||
* @param data
|
||||
*/
|
||||
async update(data: {[key: string]: EscapeValue}): Promise<QueryResult> {
|
||||
if ( !this._connection ) {
|
||||
throw new ErrorWithContext(`No connection specified to execute update query.`)
|
||||
}
|
||||
|
||||
const query = this._connection.dialect().renderUpdate(this, data)
|
||||
return this._connection.query(query)
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a DELETE based on this query.
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* query.table('my_table').where('id', <, 44).delete()
|
||||
* ```
|
||||
*
|
||||
* This is equivalent to:
|
||||
* ```sql
|
||||
* DELETE
|
||||
* FROM my_table
|
||||
* WHERE
|
||||
* id < 44
|
||||
* ```
|
||||
*
|
||||
*/
|
||||
async delete(): Promise<QueryResult> {
|
||||
if ( !this._connection ) {
|
||||
throw new ErrorWithContext(`No connection specified to execute update query.`)
|
||||
}
|
||||
|
||||
const query = this._connection.dialect().renderDelete(this)
|
||||
return this._connection.query(query)
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert the given rows into the table for this query, returning the fields specified in this query.
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const rows = [
|
||||
* { name: 'A' },
|
||||
* { name: 'B' },
|
||||
* ]
|
||||
*
|
||||
* query.table('my_table')
|
||||
* .returning('id', 'name')
|
||||
* .insert(rows)
|
||||
* ```
|
||||
*
|
||||
* This is equivalent to:
|
||||
* ```sql
|
||||
* INSERT INTO my_table (name)
|
||||
* VALUES ('A'), ('B')
|
||||
* RETURNING id, name
|
||||
* ```
|
||||
*
|
||||
* @param rowOrRows
|
||||
*/
|
||||
async insert(rowOrRows: {[key: string]: EscapeValue}|{[key: string]: EscapeValue}[]) {
|
||||
if ( !this._connection ) {
|
||||
throw new ErrorWithContext(`No connection specified to execute update query.`)
|
||||
}
|
||||
|
||||
const query = this._connection.dialect().renderInsert(this, rowOrRows)
|
||||
return this._connection.query(query)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if at least one row matches the current query.
|
||||
*/
|
||||
async exists() {
|
||||
if ( !this._connection ) {
|
||||
throw new ErrorWithContext(`No connection specified to execute update query.`)
|
||||
}
|
||||
|
||||
const query = this._connection.dialect().renderExistential(this)
|
||||
const result = await this._connection.query(query)
|
||||
return !!result.rows.first()
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a constraint to this query. This is used internally by the various `where`, `whereIn`, `orWhereNot`, &c.
|
||||
* @param preop
|
||||
* @param field
|
||||
* @param operator
|
||||
* @param operand
|
||||
* @private
|
||||
*/
|
||||
private createConstraint(preop: ConstraintConnectionOperator, field: string | ConstraintGroupClosure<T>, operator?: ConstraintOperator, operand?: any) {
|
||||
if ( typeof field === 'function' ) {
|
||||
const builder = this.getNewInstance()
|
||||
field(builder)
|
||||
this.constraints.push({
|
||||
preop,
|
||||
items: builder.appliedConstraints
|
||||
})
|
||||
} else if ( field && operator && typeof operand !== 'undefined' ) {
|
||||
this.constraints.push({
|
||||
field, operator, operand, preop, // FIXME escape operand
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
23
src/orm/builder/Builder.ts
Normal file
23
src/orm/builder/Builder.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import {ErrorWithContext} from "../../util";
|
||||
import {Container} from "../../di";
|
||||
import {ResultIterable} from "./result/ResultIterable";
|
||||
import {QueryRow} from "../types";
|
||||
import {AbstractBuilder} from "./AbstractBuilder";
|
||||
import {AbstractResultIterable} from "./result/AbstractResultIterable";
|
||||
|
||||
/**
|
||||
* Implementation of the abstract builder class that returns simple QueryRow objects.
|
||||
*/
|
||||
export class Builder extends AbstractBuilder<QueryRow> {
|
||||
public getNewInstance(): AbstractBuilder<QueryRow> {
|
||||
return Container.getContainer().make<Builder>(Builder);
|
||||
}
|
||||
|
||||
public getResultIterable(): AbstractResultIterable<QueryRow> {
|
||||
if ( !this._connection ) {
|
||||
throw new ErrorWithContext(`No connection specified to fetch iterator for query.`)
|
||||
}
|
||||
|
||||
return Container.getContainer().make<ResultIterable>(ResultIterable, this, this._connection)
|
||||
}
|
||||
}
|
||||
49
src/orm/builder/result/AbstractResultIterable.ts
Normal file
49
src/orm/builder/result/AbstractResultIterable.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import {Collection, Iterable} from "../../../util"
|
||||
import {Connection} from "../../connection/Connection";
|
||||
import {AbstractBuilder} from "../AbstractBuilder";
|
||||
|
||||
/**
|
||||
* Base Iterable class that generates the results of a Builder query.
|
||||
*/
|
||||
export abstract class AbstractResultIterable<T> extends Iterable<T> {
|
||||
protected constructor(
|
||||
/** The builder whose results should be iterated */
|
||||
public readonly builder: AbstractBuilder<T>,
|
||||
|
||||
/** The connection on which to execute the builder. */
|
||||
public readonly connection: Connection,
|
||||
) { super() }
|
||||
|
||||
/**
|
||||
* Get the SQL string for the SELECT query for this iterable.
|
||||
*/
|
||||
public abstract get selectSQL(): string
|
||||
|
||||
/**
|
||||
* Get the result at index i.
|
||||
* @param i
|
||||
*/
|
||||
public abstract at(i: number): Promise<T | undefined>
|
||||
|
||||
/**
|
||||
* Get the results starting at index `start` and ending at index `end`.
|
||||
* @param start
|
||||
* @param end
|
||||
*/
|
||||
public abstract range(start: number, end: number): Promise<Collection<T>>
|
||||
|
||||
/**
|
||||
* Count the number of results of the query.
|
||||
*/
|
||||
public abstract count(): Promise<number>
|
||||
|
||||
/**
|
||||
* Return all items resulting from this query.
|
||||
*/
|
||||
public abstract all(): Promise<Collection<T>>
|
||||
|
||||
/**
|
||||
* Create a new iterable based on this query.
|
||||
*/
|
||||
public abstract clone(): AbstractResultIterable<T>
|
||||
}
|
||||
17
src/orm/builder/result/ResultCollection.ts
Normal file
17
src/orm/builder/result/ResultCollection.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import {AsyncCollection} from "../../../util";
|
||||
import {AbstractResultIterable} from "./AbstractResultIterable";
|
||||
|
||||
/**
|
||||
* Async collection class that iterates AbstractResultIterables in chunks.
|
||||
*/
|
||||
export class ResultCollection<T> extends AsyncCollection<T> {
|
||||
constructor(
|
||||
/** The result iterable to base the collection on. */
|
||||
iterator: AbstractResultIterable<T>,
|
||||
|
||||
/** The max number of records to request per-query, by default. */
|
||||
chunkSize: number = 500
|
||||
) {
|
||||
super(iterator, chunkSize)
|
||||
}
|
||||
}
|
||||
44
src/orm/builder/result/ResultIterable.ts
Normal file
44
src/orm/builder/result/ResultIterable.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import {QueryRow} from "../../types";
|
||||
import {Builder} from "../Builder";
|
||||
import {Connection} from "../../connection/Connection";
|
||||
import {AbstractResultIterable} from "./AbstractResultIterable";
|
||||
import {Collection} from "../../../util";
|
||||
|
||||
/**
|
||||
* Implementation of AbstractResultIterable that yields simple QueryRow instances (objects).
|
||||
*/
|
||||
export class ResultIterable extends AbstractResultIterable<QueryRow> {
|
||||
constructor(
|
||||
public readonly builder: Builder,
|
||||
public readonly connection: Connection,
|
||||
) { super(builder, connection) }
|
||||
|
||||
public get selectSQL() {
|
||||
return this.connection.dialect().renderSelect(this.builder)
|
||||
}
|
||||
|
||||
async at(i: number): Promise<QueryRow | undefined> {
|
||||
const query = this.connection.dialect().renderRangedSelect(this.selectSQL, i, i + 1)
|
||||
return (await this.connection.query(query)).rows.first()
|
||||
}
|
||||
|
||||
async range(start: number, end: number): Promise<Collection<QueryRow>> {
|
||||
const query = this.connection.dialect().renderRangedSelect(this.selectSQL, start, end)
|
||||
return (await this.connection.query(query)).rows
|
||||
}
|
||||
|
||||
async count() {
|
||||
const query = this.connection.dialect().renderCount(this.selectSQL)
|
||||
const result = (await this.connection.query(query)).rows.first()
|
||||
return result?.extollo_render_count ?? 0
|
||||
}
|
||||
|
||||
async all(): Promise<Collection<QueryRow>> {
|
||||
const result = await this.connection.query(this.selectSQL)
|
||||
return result.rows
|
||||
}
|
||||
|
||||
clone() {
|
||||
return new ResultIterable(this.builder, this.connection)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user