import {ColumnMap, ColumnMapUtil} from "./column-map"; import {IJoin, JoinUtil} from "./join"; import {IColumn, ColumnUtil} from "./column"; import {IQuery} from "./query"; import {ColumnIdentifierMapUtil} from "./column-identifier-map"; import {ColumnIdentifier} from "./column-identifier"; import {Writable} from "./type"; import {Tuple} from "./tuple"; import {ColumnIdentifierRefUtil, ColumnIdentifierRef} from "./column-identifier-ref"; import {SelectItem} from "./select-item"; import {IExprSelectItem, ExprSelectItemUtil} from "./expr-select-item"; import {UnionToIntersection} from "./type"; export type ColumnRef = { readonly [tableAlias : string] : ColumnMap }; export namespace ColumnRefUtil { export type FromJoinArray = ( { readonly [tableAlias in JoinUtil.Array.TableAliases] : ( ColumnMapUtil.FromJoin< JoinUtil.Array.FindWithTableAlias > ) } ); function appendJoin( ref : Writable, join : IJoin ) { appendColumnMap(ref, ColumnMapUtil.fromJoin(join)); return ref; } function appendJoinArray( ref : Writable, arr : IJoin[] ) { for (let join of arr) { appendJoin(ref, join); } return ref; } export function fromJoinArray ( joins : JoinsT ) : ( FromJoinArray ) { return appendJoinArray({}, joins) as FromJoinArray; } //HasOneTable extends true ? // true : // false export type HasOneTable = ( Extract extends never ? //Has zero tables false : string extends Extract ? //May have zero, one, or more table boolean : ( { [tableAlias in Extract] : ( Exclude< Extract, tableAlias > ) }[Extract] ) extends never ? //Has one table true : //Has more than one table false ); export function hasOneTable ( columnRef : ColumnRefT ) : HasOneTable { return (Object.keys(columnRef).length == 1) as HasOneTable; } export type ToConvenient = ( HasOneTable extends true ? //Gives us a ColumnMap ColumnRefT[Extract] : //Gives us a ColumnRef ColumnRefT ); export function toConvenient ( columnRef : ColumnRefT ) : ToConvenient { const keys = Object.keys(columnRef) as Extract[]; if (keys.length == 1) { const result : ColumnRefT[Extract] = columnRef[keys[0]]; return result as any; } else { return columnRef as any; } } export type FromColumn = ( { readonly [tableAlias in ColumnT["tableAlias"]] : ( ColumnMapUtil.FromColumn ) } ); function appendColumn ( ref : Writable, column : IColumn ) { let map = ref[column.tableAlias]; if (map == undefined) { map = {}; ref[column.tableAlias] = map; } map[column.name] = column; return ref; } export function fromColumn ( column : ColumnT ) : FromColumn { return appendColumn({}, column) as FromColumn; } function appendExprSelectItem ( ref : Writable, item : IExprSelectItem ) { let map = ref[item.tableAlias]; if (map == undefined) { map = {}; ref[item.tableAlias] = map; } map[item.alias] = ColumnUtil.fromExprSelectItem(item); return ref; } function appendColumnMap ( ref : Writable, columnMap : ColumnMap ) { for (let columnName in columnMap) { appendColumn(ref, columnMap[columnName]); } return ref; } function appendColumnRef ( ref : Writable, columnRef : ColumnRef ) { for (let tableAlias in columnRef) { appendColumnMap(ref, columnRef[tableAlias]); } return ref; } export type FromQueryJoins< QueryT extends IQuery > = ( ( QueryT["_joins"] extends IJoin[] ? FromJoinArray : {} ) & ( QueryT["_parentJoins"] extends IJoin[] ? FromJoinArray : {} ) ); function appendQuerySelfJoins (ref : Writable, query : IQuery) { if (query._joins == undefined) { return ref; } else { return appendJoinArray(ref, query._joins); } } function appendQueryParentJoins (ref : Writable, query : IQuery) { if (query._parentJoins == undefined) { return ref; } else { return appendJoinArray(ref, query._parentJoins); } } function appendQueryJoins (ref : Writable, query : IQuery) { appendQuerySelfJoins(ref, query); appendQueryParentJoins(ref, query); } export function fromQueryJoins< QueryT extends IQuery > (query : QueryT) : FromQueryJoins { const result : Writable = {}; appendQueryJoins(result, query); return result as FromQueryJoins; } export type FromSelectItemArray_ColumnElement = ( { readonly [tableAlias in ColumnT["tableAlias"]] : { //TODO Add test case for this readonly [columnName in Extract["name"]] : ( Extract ) } } ); export type FromSelectItemArray_ExprSelectItemElement = ( { readonly [tableAlias in ExprSelectItemT["tableAlias"]] : { readonly [columnName in ExprSelectItemT["alias"]] : ( ColumnUtil.FromExprSelectItem> ) } } ); export type FromSelectItemArray_ColumnMapElement = ( { readonly [tableAlias in ColumnMapUtil.TableAlias] : { readonly [columnName in ColumnMapUtil.FindWithTableAlias< ColumnMapT, tableAlias >["name"]] : ( Extract< ColumnMapT, { [k in columnName] : ( IColumn & { tableAlias : tableAlias, name : columnName } ) } >[columnName] ) } } ); export type FromSelectItemArray_ColumnRefElement = ( //Unfortunately, {} & ColumnMap extends ColumnRef ColumnRefT[keyof ColumnRefT] extends ColumnMap ? { readonly [tableAlias in ColumnRefUtil.TableAlias] : { readonly [columnName in ColumnRefUtil.FindWithTableAlias< ColumnRefT, tableAlias >["name"]] : ( Extract< ColumnRefT, { [ta in tableAlias] : { [cn in columnName] : IColumn } } >[tableAlias][columnName] ) } } : {} ); export type FromSelectItemArray = ( ArrT[number] extends never ? {} : ( FromSelectItemArray_ColumnElement< Extract > & FromSelectItemArray_ExprSelectItemElement< Extract > & FromSelectItemArray_ColumnMapElement< Extract > & FromSelectItemArray_ColumnRefElement< Extract > ) ); function appendSelectItem ( ref : Writable, item : SelectItem ) { if (ColumnUtil.isColumn(item)) { appendColumn(ref, item); } else if (ExprSelectItemUtil.isExprSelectItem(item)) { appendExprSelectItem(ref, item); } else if (ColumnMapUtil.isColumnMap(item)) { appendColumnMap(ref, item); } else if (ColumnRefUtil.isColumnRef(item)) { appendColumnRef(ref, item); } else { throw new Error(`Unknown select item`); } return ref; } function appendSelectItemArray( ref : Writable, arr : SelectItem[] ) { for (let item of arr) { appendSelectItem(ref, item); } return ref; } export function fromSelectItemArray ( arr : ArrT ) : FromSelectItemArray { const result : Writable = {}; appendSelectItemArray(result, arr); return result as FromSelectItemArray; } export type FromQuerySelects = ( QueryT["_selects"] extends SelectItem[] ? FromSelectItemArray : {} ); export function fromQuerySelects ( query : QueryT ) : FromQuerySelects { const result : ColumnRef = {}; if (query._selects != undefined) { appendSelectItemArray(result, query._selects); } return result as FromQuerySelects; } export type FromQuery = ( FromQueryJoins & FromQuerySelects ); export function fromQuery ( query : QueryT ) : FromQuery { const result : ColumnRef = {}; appendQueryJoins(result, query); if (query._selects != undefined) { appendSelectItemArray(result, query._selects); } return result as FromQuery; } //TODO This belongs to ColumnRefIdentifierUtil export function assertIsSubset (a : ColumnIdentifierRef, b : ColumnIdentifierRef) { for (let tableAliasA in a) { const columnMapA = a[tableAliasA]; const columnMapB = b[tableAliasA]; if (columnMapB == undefined) { throw new Error(`Table ${tableAliasA} is not allowed`); } ColumnIdentifierMapUtil.assertIsSubset(columnMapA, columnMapB); } } export type HasColumnIdentifier< ColumnRefT extends ColumnRef, ColumnIdentifierT extends ColumnIdentifier > = ( ColumnIdentifierRefUtil.HasColumnIdentifier ); export function hasColumnIdentifier< ColumnRefT extends ColumnRef, ColumnIdentifierT extends ColumnIdentifier > (columnRef : ColumnRefT, columnIdentifier : ColumnIdentifierT) : ( HasColumnIdentifier ) { return ColumnIdentifierRefUtil.hasColumnIdentifier(columnRef, columnIdentifier); } export function assertHasColumnIdentifier (columnRef : ColumnRef, columnIdentifier : ColumnIdentifier) { ColumnIdentifierRefUtil.assertHasColumnIdentifier(columnRef, columnIdentifier); } export function assertHasColumnIdentifiers (columnRef : ColumnRef, columnIdentifiers : ColumnIdentifier[]) { ColumnIdentifierRefUtil.assertHasColumnIdentifiers(columnRef, columnIdentifiers); } export type FromColumnArray = ( { readonly [tableAlias in ColumnsT[number]["tableAlias"]] : ( ColumnMapUtil.FromColumnArray< Extract[] > ) } ); export function fromColumnArray ( columns : ColumnsT ) : FromColumnArray { const result : Writable = {}; for (let column of columns) { let columnMap : undefined|Writable = result[column.tableAlias]; if (columnMap == undefined) { columnMap = {}; result[column.tableAlias] = columnMap; } columnMap[column.name] = column; } return result as FromColumnArray; } //Take the intersection and the "left" columnRef export type LeftIntersect< ColumnRefA extends ColumnRef, ColumnRefB extends ColumnRef > = ( { readonly [tableAlias in Extract] : ( tableAlias extends keyof ColumnRefB ? ColumnMapUtil.Intersect< ColumnRefA[tableAlias], ColumnRefB[tableAlias] > : ColumnRefA[tableAlias] ) } ); export type Intersect< ColumnRefA extends ColumnRef, ColumnRefB extends ColumnRef > = ( Extract< LeftIntersect & { readonly [tableAlias in Exclude< Extract, keyof ColumnRefA >] : ( ColumnRefB[tableAlias] ) }, ColumnRef > ); export function intersect< ColumnRefA extends ColumnRef, ColumnRefB extends ColumnRef > ( columnRefA : ColumnRefA, columnRefB : ColumnRefB ) : Intersect { const result : Writable = {}; for (let tableAlias in columnRefA) { if (columnRefB.hasOwnProperty(tableAlias)) { result[tableAlias] = ColumnMapUtil.intersect( columnRefA[tableAlias], columnRefB[tableAlias] ); } else { result[tableAlias] = columnRefA[tableAlias]; } } for (let tableAlias in columnRefB) { if (!columnRefA.hasOwnProperty(tableAlias)) { result[tableAlias] = columnRefB[tableAlias]; } } return result as Intersect; } /* s = `ArrT[0]` arr = []; arr.push(s); for (let i=1; i<5; ++i) { s = `Intersect<${s}, ArrT[${i}]>`; arr.push(s); } arr2 = []; for (let i=0; i = ( ArrT[number] extends never ? {} : Extract< UnionToIntersection< ArrT[number] >, ColumnRef > /* ArrT["length"] extends 0 ? {} : ArrT["length"] extends 1 ? ArrT[0] : ArrT["length"] extends 2 ? ArrT[0] & ArrT[1] : ArrT["length"] extends 3 ? ArrT[0] & ArrT[1] & ArrT[2] : ArrT["length"] extends 4 ? ArrT[0] & ArrT[1] & ArrT[2] & ArrT[3] : ArrT["length"] extends 5 ? ArrT[0] & ArrT[1] & ArrT[2] & ArrT[3] & ArrT[4] : ArrT["length"] extends 6 ? ArrT[0] & ArrT[1] & ArrT[2] & ArrT[3] & ArrT[4] & ArrT[5] : ArrT["length"] extends 7 ? ArrT[0] & ArrT[1] & ArrT[2] & ArrT[3] & ArrT[4] & ArrT[5] & ArrT[6] : ArrT["length"] extends 8 ? ArrT[0] & ArrT[1] & ArrT[2] & ArrT[3] & ArrT[4] & ArrT[5] & ArrT[6] & ArrT[7] : ArrT["length"] extends 9 ? ArrT[0] & ArrT[1] & ArrT[2] & ArrT[3] & ArrT[4] & ArrT[5] & ArrT[6] & ArrT[7] & ArrT[8] : ArrT["length"] extends 10 ? ArrT[0] & ArrT[1] & ArrT[2] & ArrT[3] & ArrT[4] & ArrT[5] & ArrT[6] & ArrT[7] & ArrT[8] & ArrT[9] : ArrT["length"] extends 11 ? ArrT[0] & ArrT[1] & ArrT[2] & ArrT[3] & ArrT[4] & ArrT[5] & ArrT[6] & ArrT[7] & ArrT[8] & ArrT[9] & ArrT[10] : ArrT["length"] extends 12 ? ArrT[0] & ArrT[1] & ArrT[2] & ArrT[3] & ArrT[4] & ArrT[5] & ArrT[6] & ArrT[7] & ArrT[8] & ArrT[9] & ArrT[10] & ArrT[11] : ArrT["length"] extends 13 ? ArrT[0] & ArrT[1] & ArrT[2] & ArrT[3] & ArrT[4] & ArrT[5] & ArrT[6] & ArrT[7] & ArrT[8] & ArrT[9] & ArrT[10] & ArrT[11] & ArrT[12] : ArrT["length"] extends 14 ? ArrT[0] & ArrT[1] & ArrT[2] & ArrT[3] & ArrT[4] & ArrT[5] & ArrT[6] & ArrT[7] & ArrT[8] & ArrT[9] & ArrT[10] & ArrT[11] & ArrT[12] & ArrT[13] : ArrT["length"] extends 15 ? ArrT[0] & ArrT[1] & ArrT[2] & ArrT[3] & ArrT[4] & ArrT[5] & ArrT[6] & ArrT[7] & ArrT[8] & ArrT[9] & ArrT[10] & ArrT[11] & ArrT[12] & ArrT[13] & ArrT[14] : ArrT["length"] extends 16 ? ArrT[0] & ArrT[1] & ArrT[2] & ArrT[3] & ArrT[4] & ArrT[5] & ArrT[6] & ArrT[7] & ArrT[8] & ArrT[9] & ArrT[10] & ArrT[11] & ArrT[12] & ArrT[13] & ArrT[14] & ArrT[15] : ArrT["length"] extends 17 ? ArrT[0] & ArrT[1] & ArrT[2] & ArrT[3] & ArrT[4] & ArrT[5] & ArrT[6] & ArrT[7] & ArrT[8] & ArrT[9] & ArrT[10] & ArrT[11] & ArrT[12] & ArrT[13] & ArrT[14] & ArrT[15] & ArrT[16] : ArrT["length"] extends 18 ? ArrT[0] & ArrT[1] & ArrT[2] & ArrT[3] & ArrT[4] & ArrT[5] & ArrT[6] & ArrT[7] & ArrT[8] & ArrT[9] & ArrT[10] & ArrT[11] & ArrT[12] & ArrT[13] & ArrT[14] & ArrT[15] & ArrT[16] & ArrT[17] : ArrT["length"] extends 19 ? ArrT[0] & ArrT[1] & ArrT[2] & ArrT[3] & ArrT[4] & ArrT[5] & ArrT[6] & ArrT[7] & ArrT[8] & ArrT[9] & ArrT[10] & ArrT[11] & ArrT[12] & ArrT[13] & ArrT[14] & ArrT[15] & ArrT[16] & ArrT[17] & ArrT[18] : ArrT["length"] extends 20 ? ArrT[0] & ArrT[1] & ArrT[2] & ArrT[3] & ArrT[4] & ArrT[5] & ArrT[6] & ArrT[7] & ArrT[8] & ArrT[9] & ArrT[10] & ArrT[11] & ArrT[12] & ArrT[13] & ArrT[14] & ArrT[15] & ArrT[16] & ArrT[17] & ArrT[18] & ArrT[19] : //add more lengths //Too many to handle... ColumnRef */ ); export function intersectTuple> ( ...arr : ArrT ) : IntersectTuple { let result : ColumnRef = {}; for (let columnRef of arr) { result = intersect(result, columnRef); } return result as IntersectTuple; } export type ToPartial = ( { readonly [tableAlias in Extract]? : { readonly [columnName in Extract]? : ( RefT[tableAlias][columnName] ) } } ); export function isColumnRef (raw : any) : raw is ColumnRef { if (!(raw instanceof Object)) { return false; } if (raw instanceof Array) { return false; } if (raw instanceof Function) { return false; } if (raw instanceof Date) { return false; } for (let tableAlias in raw) { const columnMap = raw[tableAlias]; if (!ColumnMapUtil.isColumnMap(columnMap)) { return false; } } return true; } export type TableAlias = ( RefT extends ColumnRef ? Extract : never ); export type FindWithTableAlias = ( RefT extends ColumnRef ? ColumnMapUtil.FindWithTableAlias< RefT[Extract], TableAliasT > : never ); export type FindWithColumnName = ( RefT extends ColumnRef ? ColumnMapUtil.FindWithColumnName< RefT[Extract], ColumnNameT > : never ); export function getSortedColumnArray (columnRef : ColumnRef) : IColumn[] { const tableAliases = Object.keys(columnRef); tableAliases.sort(); const result : IColumn[] = []; for (let tableAlias of tableAliases) { result.push(...ColumnMapUtil.getSortedColumnArray(columnRef[tableAlias])); } return result; } export type DuplicateColumnName = ( { [tableAlias in Extract] : ( Extract< //Get the column names of this ColumnMap ColumnMapUtil.ColumnNames< RefT[tableAlias] >, //Get the column names of all other ColumnMap ColumnMapUtil.ColumnNames< RefT[Exclude< Extract, tableAlias >] > > ) }[Extract] ); export type HasDuplicateColumnName = ( DuplicateColumnName extends never ? false : true ); export type ToInterface = ( { readonly [tableAlias in keyof RefT] : ( ColumnMapUtil.ToInterface ) } ); //Technically, this could be wrong. //But it shouldn't be wrong, in general. export type ColumnNames = ( RefT extends ColumnRef ? ColumnMapUtil.ColumnNames< RefT[Extract] > : never ); export function columnNames ( columnRef : RefT ) : ColumnNames[] { const result = Object.keys(columnRef).reduce( (memo, tableAlias) => { memo.push(...ColumnMapUtil.columnNames(columnRef[tableAlias])); return memo; }, [] ); return result as ColumnNames[]; } }