An elegant object-relational mapping that lifts up your code.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

169 lines
5.4 KiB

import {AppClass} from '@extollo/lib';
import {Constraint} from "../types";
import {AbstractBuilder} from "../builder/AbstractBuilder";
/**
* 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: any,
/** The query-safe sanitized value. */
public readonly value: any,
) { }
/** Cast the value to a query-safe string. */
toString() {
return 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: any) {
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;
}