|
|
|
import {Constraint} from '../types'
|
|
|
|
import {AbstractBuilder} from '../builder/AbstractBuilder'
|
|
|
|
import {AppClass} from '../../lifecycle/AppClass'
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A value which can be escaped to be interpolated into an SQL query.
|
|
|
|
*/
|
|
|
|
export type EscapeValue = null | undefined | string | number | boolean | Date | QuerySafeValue | EscapeValue[] // FIXME | Select<any>
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Object mapping string field names to EscapeValue items.
|
|
|
|
*/
|
|
|
|
export type EscapeValueObject = { [field: string]: EscapeValue }
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A wrapper class whose value is save to inject directly into a query.
|
|
|
|
*/
|
|
|
|
export class QuerySafeValue {
|
|
|
|
constructor(
|
|
|
|
/** The unescaped value. */
|
|
|
|
public readonly originalValue: unknown,
|
|
|
|
|
|
|
|
/** The query-safe sanitized value. */
|
|
|
|
public readonly value: unknown,
|
|
|
|
) { }
|
|
|
|
|
|
|
|
/** Cast the value to a query-safe string. */
|
|
|
|
toString(): string {
|
|
|
|
return String(this.value)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Treat the value as raw SQL that can be injected directly into a query.
|
|
|
|
* This is dangerous and should NEVER be used to wrap user input.
|
|
|
|
* @param value
|
|
|
|
*/
|
|
|
|
export function raw(value: unknown): QuerySafeValue {
|
|
|
|
return new QuerySafeValue(value, value)
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Abstract class defining a particular dialect of SQL that is used to render
|
|
|
|
* query builders to strings of SQL of that dialect for execution by Connection
|
|
|
|
* instances.
|
|
|
|
*/
|
|
|
|
export abstract class SQLDialect extends AppClass {
|
|
|
|
/**
|
|
|
|
* Escape the given value and return the query-safe equivalent.
|
|
|
|
* @param value
|
|
|
|
*/
|
|
|
|
public abstract escape(value: EscapeValue): QuerySafeValue
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Render the given query builder as a "SELECT ..." query string.
|
|
|
|
*
|
|
|
|
* This function should escape the values before they are included in the query string.
|
|
|
|
* @param builder
|
|
|
|
*/
|
|
|
|
public abstract renderSelect(builder: AbstractBuilder<any>): string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Render the given query builder as an "UPDATE ..." query string, setting the
|
|
|
|
* column values from the given data object.
|
|
|
|
*
|
|
|
|
* This function should escape the values before they are included in the query string.
|
|
|
|
* @param builder
|
|
|
|
* @param data
|
|
|
|
*/
|
|
|
|
public abstract renderUpdate(builder: AbstractBuilder<any>, data: {[key: string]: EscapeValue}): string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Render the given query builder as a "DELETE ..." query string.
|
|
|
|
*
|
|
|
|
* This function should escape the values before they are included in the query string.
|
|
|
|
* @param builder
|
|
|
|
*/
|
|
|
|
public abstract renderDelete(builder: AbstractBuilder<any>): string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Render the given query builder as a query that can be used to test if at
|
|
|
|
* least 1 row exists for the given builder.
|
|
|
|
*
|
|
|
|
* The resultant query should return at least 1 row if that condition is met,
|
|
|
|
* and should return NO rows otherwise.
|
|
|
|
*
|
|
|
|
* This function should escape the values before they are included in the query string.
|
|
|
|
*
|
|
|
|
* @example
|
|
|
|
* The PostgreSQL dialect achieves this by removing the user-specified fields,
|
|
|
|
* select-ing `TRUE`, and applying `LIMIT 1` to the query. This returns a single
|
|
|
|
* row if the constraints have results, and nothing otherwise.
|
|
|
|
*
|
|
|
|
* @param builder
|
|
|
|
*/
|
|
|
|
public abstract renderExistential(builder: AbstractBuilder<any>): string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Render the given query as an "INSERT ..." query string, inserting rows for
|
|
|
|
* the given data object(s).
|
|
|
|
*
|
|
|
|
* This function should escape the values before they are included in the query string.
|
|
|
|
*
|
|
|
|
* @param builder
|
|
|
|
* @param data
|
|
|
|
*/
|
|
|
|
public abstract renderInsert(builder: AbstractBuilder<any>, data: {[key: string]: EscapeValue}|{[key: string]: EscapeValue}[]): string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Wrap the given query string as a "SELECT ..." query that returns the number of
|
|
|
|
* rows matched by the original query string.
|
|
|
|
*
|
|
|
|
* The resultant query should return the `extollo_render_count` field with the
|
|
|
|
* number of rows that the original `query` would return.
|
|
|
|
*
|
|
|
|
* @param query
|
|
|
|
*/
|
|
|
|
public abstract renderCount(query: string): string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Given a rendered "SELECT ..." query string, wrap it such that the query will
|
|
|
|
* only return the rows ranging from the `start` to `end` indices.
|
|
|
|
*
|
|
|
|
* @param query
|
|
|
|
* @param start
|
|
|
|
* @param end
|
|
|
|
*/
|
|
|
|
public abstract renderRangedSelect(query: string, start: number, end: number): string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Given an array of Constraint objects, render them as WHERE-clause SQL in this dialect.
|
|
|
|
*
|
|
|
|
* This function should escape the values before they are included in the query string.
|
|
|
|
*
|
|
|
|
* @example
|
|
|
|
* ```ts
|
|
|
|
* dialect.renderConstraints([
|
|
|
|
* {
|
|
|
|
* field: 'id',
|
|
|
|
* operator: '<',
|
|
|
|
* operand: 44,
|
|
|
|
* preop: 'AND',
|
|
|
|
* },
|
|
|
|
* {
|
|
|
|
* field: 'id',
|
|
|
|
* operator: '>',
|
|
|
|
* operand: 30,
|
|
|
|
* preop: 'AND',
|
|
|
|
* },
|
|
|
|
* ]) // => 'id < 44 AND id > 30'
|
|
|
|
* ```
|
|
|
|
*
|
|
|
|
* @param constraints
|
|
|
|
*/
|
|
|
|
public abstract renderConstraints(constraints: Constraint[]): string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Render the "SET ... [field = value ...]" portion of the update query.
|
|
|
|
*
|
|
|
|
* This function should escape the values before they are included in the query string.
|
|
|
|
*
|
|
|
|
* @example
|
|
|
|
* dialect.renderUpdateSet({field1: 'value', field2: 45})
|
|
|
|
* // => "SET field1 = 'value', field2 = 45"
|
|
|
|
*
|
|
|
|
* @param data
|
|
|
|
*/
|
|
|
|
public abstract renderUpdateSet(data: {[key: string]: EscapeValue}): string;
|
|
|
|
}
|