import { Col, colUnwrap } from "./Column"; import { SomeCol } from "./Exp"; import { GenState, Scope } from "./GenState"; import { ConflictTarget, conflictTargetTableColumns } from "./OnConflict"; import { MakeCols, MakeTable, toTup, Write } from "./Query"; import { Query, runQueryM } from "./Query/Type"; import { Param, SQL } from "./SQL"; import { compDelete, compInsert, compUpdate } from "./SQL/Print"; import { Table } from "./Table"; import { allNonOutputColNames, colNames, removeDeadCols, state2sql } from "./Transform"; import { ColName } from "./Types"; /** * Compile a query to an SQL AST. * * Groups are ignored, as they are only used by `aggregate`. */ export function compQuery(scope: Scope, q: Query): SQL { const [cs, st] = runQueryM(scope, q); return compQuery2(cs, st); } export function compQuery2(cs: a, st: GenState): SQL { const final = finalCols(cs); const sql = state2sql(st); const live = colNames(final).concat(allNonOutputColNames(sql)); const srcs = removeDeadCols(live, sql); const s: SQL = { cols: final, source: { type: "Product", sqls: [srcs] }, restricts: [], groups: [], ordering: [], limits: null, distinct: false }; return s; } let scopeSupply: Scope = 1; export function freshScope(): Scope { scopeSupply++; return scopeSupply - 1; } export function resetScope(): void { scopeSupply = 1; } let globalNameSupply: number = 1; export function nextGlobalNameSupply(): number { globalNameSupply++; return globalNameSupply - 1; } export function resetGlobalNameSupply(): void { globalNameSupply = 0; } /** * @param cols object whose fields are all of type Col */ export function finalCols(cols: object): SomeCol[] { const result: SomeCol[] = []; const keys = Object.keys(cols); keys.sort(); for (const key of keys) { const col: Col = (cols)[key]; result.push({ type: "Some", exp: colUnwrap(col), parser: (colUnwrap(col)).parser }); // This is needed so that we later know which propNames to use when // constructing the results of a query. (See the `query` function) (result[result.length - 1]).propName = key; } return result; } export function compileInsert(tbl: Table, rowValues: MakeTable[], conflictTarget: ConflictTarget | undefined, conflictAction: [(c: MakeCols) => Col, (c: MakeCols) => MakeTable] | undefined, returning: (c: MakeCols) => MakeCols): [string, Param[]] { const names = tbl.tableCols.map<[ColName, string, (val: string) => any]>(x => [x.name, x.propName, x.parser]); const cs = toTup(names, null); // tslint:disable-line:no-unnecessary-type-assertion const fs = rowValues.map(finalCols); const rs = finalCols(returning(cs)); if (conflictTarget === undefined) { return compInsert(tbl.tableName, names, fs, undefined, undefined, undefined, rs); } else { const conflictTblCols = conflictTargetTableColumns(conflictTarget, tbl); if (conflictAction === undefined) { return compInsert(tbl.tableName, names, fs, conflictTblCols, undefined, undefined, rs); } else { const [check, upd] = conflictAction; const names = tbl.tableCols.map<[ColName, string, (val: string) => any]>(x => [x.name, x.propName, x.parser]); const cs = toTup(names, tbl.tableName); const updated: [ColName, SomeCol][] = []; const fs2 = finalCols(upd(cs)); for (let i = 0; i < names.length; ++i) { updated.push([ names[i][0], fs2[i] ]); } const predicate = colUnwrap(check(cs)); return compInsert(tbl.tableName, names, fs, conflictTblCols, predicate, updated, rs); } } } export function compileUpdate(tbl: Table, check: (c: MakeCols) => Col, upd: (c: MakeCols) => MakeTable, returning: ((c: MakeCols) => MakeCols) | undefined): [string, Param[]] { const names = tbl.tableCols.map<[ColName, string, (val: string) => any]>(x => [x.name, x.propName, x.parser]); const cs = toTup(names, null); const updated: [ColName, SomeCol][] = []; const fs = finalCols(upd(cs)); for (let i = 0; i < names.length; ++i) { updated.push([ names[i][0], fs[i] ]); } const predicate = colUnwrap(check(cs)); if (returning === undefined) { return compUpdate(tbl.tableName, predicate, updated, []); } else { const cs = toTup(names, null); // tslint:disable-line:no-unnecessary-type-assertion const rs = finalCols(returning(cs)); return compUpdate(tbl.tableName, predicate, updated, rs); } } export function compileDelete(tbl: Table, check: (c: MakeCols) => Col): [string, Param[]] { const names = tbl.tableCols.map<[ColName, string, (val: string) => any]>(x => [x.name, x.propName, x.parser]); const cs = toTup(names, null); const predicate = colUnwrap(check(cs)); return compDelete(tbl.tableName, predicate); }