UNPKG

phantasy-mysql

Version:
267 lines (221 loc) 8.04 kB
// @flow import type { QueryCriteria, QueryObj, QueryValues } from './sql/types'; import { limit } from './sql/limit'; import { offset } from './sql/offset'; import { where } from './sql/where'; /** * `Query` class */ export class Query<Props: {}> { props: Props constructor(props: Props): void { this.props = props; } } /** * `SelectQuery` class */ export class SelectQuery<Props: {}> extends Query<Props> { fields(field: string, ...fields: Array<string>): SelectQuery<Props & { fields: Array<string> }> { return new SelectQuery({ ...this.props, fields: [ field, ...fields ] }); } from(table: string): SelectQuery<Props & { table: string }> { return new SelectQuery({ ...this.props, table }); } where(...criteria: QueryCriteria[]): SelectQuery<Props & { where: QueryCriteria[] }> { return new SelectQuery({ ...this.props, where: criteria }); } limit(n: number): SelectQuery<Props & { limit: number }> { return new SelectQuery({ ...this.props, limit: n }); } offset(n: number): SelectQuery<Props & { offset: number }> { return new SelectQuery({ ...this.props, offset: n }); } } /** * `InsertQuery` class */ export class InsertQuery<Props: { data: QueryValues[] }> extends Query<Props> { into(table: string): InsertQuery<Props & { table: string }> { return new InsertQuery({ ...this.props, table }); } } /** * `ReplaceQuery` class */ export class ReplaceQuery<Props: { data: QueryValues[] }> extends Query<Props> { into(table: string): ReplaceQuery<Props & { table: string }> { return new ReplaceQuery({ ...this.props, table }); } } /** * `UpdateQuery` class */ export class UpdateQuery<Props: {}> extends Query<Props> { table(table: string) { return new UpdateQuery({ ...this.props, table }); } set(data: QueryValues): UpdateQuery<Props & { data: QueryValues }> { return new UpdateQuery({ ...this.props, data }); } where(...criteria: QueryCriteria[]): UpdateQuery<Props & { where: QueryCriteria[] }> { return new UpdateQuery({ ...this.props, where: criteria }); } limit(n: number): UpdateQuery<Props & { limit: number }> { return new UpdateQuery({ ...this.props, limit: n }); } } /** * `DeleteQuery` class */ export class DeleteQuery<Props: {}> extends Query<Props> { from(table: string): DeleteQuery<Props & { table: string }> { return new DeleteQuery({ ...this.props, table }); } where(...criteria: QueryCriteria[]): DeleteQuery<Props & { where: QueryCriteria[] }> { return new DeleteQuery({ ...this.props, where: criteria }); } limit(n: number): DeleteQuery<Props & { limit: number }> { return new DeleteQuery({ ...this.props, limit: n }); } offset(n: number): DeleteQuery<Props & { offset: number }> { return new DeleteQuery({ ...this.props, offset: n }); } } export type BuiltQuery = // SELECT | SelectQuery<{ table: string, fields: string[] }> | SelectQuery<{ table: string, fields: string[], where: QueryCriteria[] }> | SelectQuery<{ table: string, fields: string[], limit: number }> | SelectQuery<{ table: string, fields: string[], offset: number }> | SelectQuery<{ table: string, fields: string[], where: QueryCriteria[], limit: number }> | SelectQuery<{ table: string, fields: string[], where: QueryCriteria[], offset: number }> | SelectQuery<{ table: string, fields: string[], limit: number, offset: number }> | SelectQuery<{ table: string, fields: string[], where: QueryCriteria[], limit: number, offset: number }> // INSERT | InsertQuery<{ table: string, data: QueryValues[] }> // REPLACE | ReplaceQuery<{ table: string, data: QueryValues[] }> // UPDATE | UpdateQuery<{ table: string, data: QueryValues }> | UpdateQuery<{ table: string, data: QueryValues, where: QueryCriteria[] }> | UpdateQuery<{ table: string, data: QueryValues, limit: number }> | UpdateQuery<{ table: string, data: QueryValues, where: QueryCriteria[], limit: number }> // DELETE | DeleteQuery<{ table: string }> | DeleteQuery<{ table: string, where: QueryCriteria[] }> | DeleteQuery<{ table: string, limit: number }> | DeleteQuery<{ table: string, where: QueryCriteria[], limit: number }> | DeleteQuery<{ table: string, offset: number }> | DeleteQuery<{ table: string, where: QueryCriteria[], offset: number }> | DeleteQuery<{ table: string, limit: number, offset: number }> | DeleteQuery<{ table: string, where: QueryCriteria[], limit: number, offset: number }> /** * `toSql :: BuiltQuery -> QueryObj` */ export function toSql(q: BuiltQuery): QueryObj { if (q instanceof SelectQuery) { const props = q.props, { sql: limitSql, args: limitArgs } = limit(props), { sql: offsetSql, args: offsetArgs } = offset(props), { sql: whereSql, args: whereArgs } = where(props); return { sql: `SELECT ${ props.fields.join(', ') } FROM ${ props.table } ${ [ whereSql, limitSql, offsetSql ].filter(sql => sql.length > 0).join(' ') }`.trim(), args: [ ...whereArgs, ...limitArgs, ...offsetArgs ] }; } else if (q instanceof InsertQuery) { const props = q.props, records = props.data, fields = Object.keys(records[0]); const recordSql = records.map(record => { const placeholders = Object.keys(record).map(() => `?`); return `(${ placeholders.join(', ') })`; }) .join(', '); const args = records.map(record => { return Object.keys(record).map(key => record[key]); }) .reduce((args, recordArgs) => [ ...args, ...recordArgs ], []); return { sql: `INSERT INTO ${ props.table } (${ fields.join(', ') }) VALUES ${ recordSql }`.trim(), args }; } else if (q instanceof ReplaceQuery) { const props = q.props, records = props.data, fields = Object.keys(records[0]); const recordSql = records.map(record => { const placeholders = Object.keys(record).map(() => `?`); return `(${ placeholders.join(', ') })`; }) .join(', '); const args = records.map(record => { return Object.keys(record).map(key => record[key]); }) .reduce((args, recordArgs) => [ ...args, ...recordArgs ], []); return { sql: `REPLACE INTO ${ props.table } (${ fields.join(', ') }) VALUES ${ recordSql }`.trim(), args }; } else if (q instanceof UpdateQuery) { const props = q.props, keys = Object.keys(props.data), setSql = keys.map(key => `${ key } = ?`).join(', '), setArgs = keys.map(key => props.data[key]), { sql: limitSql, args: limitArgs } = limit(props), { sql: whereSql, args: whereArgs } = where(props); return { sql: `UPDATE ${ props.table } SET ${ setSql } ${ [ whereSql, limitSql ].filter(sql => sql.length > 0).join(' ') }`.trim(), args: [ ...setArgs, ...whereArgs, ...limitArgs ] }; } else if (q instanceof DeleteQuery) { const props = q.props, { sql: limitSql, args: limitArgs } = limit(props), { sql: offsetSql, args: offsetArgs } = offset(props), { sql: whereSql, args: whereArgs } = where(props); return { sql: `DELETE FROM ${ props.table } ${ [ whereSql, limitSql, offsetSql ].filter(sql => sql.length > 0).join(' ') }`.trim(), args: [ ...whereArgs, ...limitArgs, ...offsetArgs ] }; } else { /* istanbul ignore next */ (q: empty); // eslint-disable-line no-unused-expressions /* istanbul ignore next */ throw new TypeError(); } } /** * `Select :: string[] -> SelectQuery` */ export function Select(): SelectQuery<{}> { return new SelectQuery({}); } /** * `Insert :: QueryValues[] -> InsertQuery` */ export function Insert(data: QueryValues, ...datas: QueryValues[]): InsertQuery<{ data: QueryValues[] }> { return new InsertQuery({ data: [ data, ...datas ] }); } /** * `Replace :: QueryValues[] -> ReplaceQuery` */ export function Replace(data: QueryValues, ...datas: QueryValues[]): ReplaceQuery<{ data: QueryValues[] }> { return new ReplaceQuery({ data: [ data, ...datas ] }); } /** * `Update :: string -> UpdateQuery` */ export function Update(): UpdateQuery<{}> { return new UpdateQuery({}); } /** * `Delete :: () -> DeleteQuery` */ export function Delete(..._: void[]): DeleteQuery<{}> { // eslint-disable-line no-unused-vars return new DeleteQuery({}); }