-
-
Notifications
You must be signed in to change notification settings - Fork 94
/
union.ts
117 lines (101 loc) · 3.62 KB
/
union.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
import { IValue, _ISelection, _Transaction, _Explainer, _SelectExplanation, Stats, _IIndex, _IType, setId, getId } from '../interfaces-private.ts';
import { DataSourceBase } from './transform-base.ts';
import { ColumnNotFound, nil, NotSupported, QueryError } from '../interfaces.ts';
import { columnEvaluator } from './selection.ts';
import { reconciliateTypes } from '../datatypes/datatypes.ts';
import { ExprRef } from 'https://deno.land/x/pgsql_ast_parser@12.0.1/mod.ts';
import { colByName } from '../utils.ts';
// https://www.postgresql.org/docs/current/typeconv-union-case.html
export function buildUnion(left: _ISelection, right: _ISelection) {
if (left.columns.length !== right.columns.length) {
throw new QueryError('each UNION query must have the same number of columns');
}
const cols: UCol[] = Array(left.columns.length);
for (let i = 0; i < left.columns.length; i++) {
const l = left.columns[i];
const r = right.columns[i];
const type = reconciliateTypes([l, r], true);
if (!type) {
throw new QueryError(`UNION types ${l.type.name} and ${r.type.name} cannot be matched`);
}
cols[i] = {
name: l.id ?? ('column' + i),
type,
lval: l.cast(type),
rval: r.cast(type),
};
}
return new Union(cols, left, right);
}
interface UCol {
name: string;
type: _IType;
lval: IValue;
rval: IValue;
}
class Union<T = any> extends DataSourceBase<T> {
get isExecutionWithNoResult(): boolean {
return false;
}
isAggregation() {
return false;
}
readonly columns: ReadonlyArray<IValue<any>>;
private readonly colsByName = new Map<string, IValue>();
entropy(t: _Transaction) {
return this.left.entropy(t) + this.right.entropy(t);
}
hasItem(raw: T, t: _Transaction): boolean {
return this.left.hasItem(raw, t) || this.right.hasItem(raw, t);
}
constructor(private cols: UCol[]
, private left: _ISelection
, private right: _ISelection) {
super(left.ownerSchema);
this.columns = cols.map(x => columnEvaluator(this, x.name, x.type));
for (const c of this.columns) {
this.colsByName.set(c.id!, c);
}
}
stats(t: _Transaction): Stats | null {
return null;
}
*enumerate(t: _Transaction): Iterable<T> {
for (const raw of this.left.enumerate(t)) {
const ret = {} as any;
setId(ret, getId(raw));
for (const c of this.cols) {
ret[c.name] = c.lval.get(raw, t);
}
yield ret;
}
for (const raw of this.right.enumerate(t)) {
const ret = {} as any;
setId(ret, getId(raw));
for (const c of this.cols) {
ret[c.name] = c.rval.get(raw, t);
}
yield ret;
}
}
explain(e: _Explainer): _SelectExplanation {
return {
id: e.idFor(this),
_: 'union',
union: [this.left.explain(e),
this.right.explain(e)],
};
}
getColumn(column: string | ExprRef): IValue;
getColumn(column: string | ExprRef, nullIfNotFound?: boolean): IValue | nil;
getColumn(column: string | ExprRef, nullIfNotFound?: boolean): IValue<any> | nil {
return colByName(this.colsByName, column, nullIfNotFound);
}
getIndex(...forValue: IValue<any>[]): _IIndex<any> | null | undefined {
// todo use indices on unions
return null;
}
isOriginOf(a: IValue<any>): boolean {
return a.origin === this || this.left.isOriginOf(a);
}
}