• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright (C) 2024 The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15import {SourceDataset, UnionDataset} from './dataset';
16import {
17  BLOB,
18  BLOB_NULL,
19  LONG,
20  LONG_NULL,
21  NUM,
22  NUM_NULL,
23  STR,
24  STR_NULL,
25  UNKNOWN,
26} from './query_result';
27
28test('get query for simple dataset', () => {
29  const dataset = new SourceDataset({
30    src: 'slice',
31    schema: {id: NUM},
32  });
33
34  expect(dataset.query()).toEqual('select id from (slice)');
35});
36
37test("get query for simple dataset with 'eq' filter", () => {
38  const dataset = new SourceDataset({
39    src: 'slice',
40    schema: {id: NUM},
41    filter: {
42      col: 'id',
43      eq: 123,
44    },
45  });
46
47  expect(dataset.query()).toEqual('select id from (slice) where id = 123');
48});
49
50test("get query for simple dataset with an 'in' filter", () => {
51  const dataset = new SourceDataset({
52    src: 'slice',
53    schema: {id: NUM},
54    filter: {
55      col: 'id',
56      in: [123, 456],
57    },
58  });
59
60  expect(dataset.query()).toEqual(
61    'select id from (slice) where id in (123,456)',
62  );
63});
64
65test('get query for union dataset', () => {
66  const dataset = new UnionDataset([
67    new SourceDataset({
68      src: 'slice',
69      schema: {id: NUM},
70      filter: {
71        col: 'id',
72        eq: 123,
73      },
74    }),
75    new SourceDataset({
76      src: 'slice',
77      schema: {id: NUM},
78      filter: {
79        col: 'id',
80        eq: 456,
81      },
82    }),
83  ]);
84
85  expect(dataset.query()).toEqual(
86    'select id from (slice) where id = 123\nunion all\nselect id from (slice) where id = 456',
87  );
88});
89
90test('union dataset batches large numbers of unions', () => {
91  const datasets = [];
92  for (let i = 0; i < 800; i++) {
93    datasets.push(
94      new SourceDataset({
95        src: 'foo',
96        schema: {bar: NUM},
97        filter: {
98          col: 'some_id',
99          eq: i,
100        },
101      }),
102    );
103  }
104
105  const query = new UnionDataset(datasets).query();
106
107  // Verify query structure with CTE batching.
108  expect(query).toContain('with');
109
110  // Should have at least 2 CTE batches.
111  expect(query).toContain('union_batch_0 as');
112  expect(query).toContain('union_batch_1 as');
113
114  // 798 union alls within batches (for 800 datasets) + 1 union alls between the
115  // 2 CTEs.
116  const batchMatches = query.match(/union all/g);
117  expect(batchMatches?.length).toBe(799);
118});
119
120test('implements', () => {
121  const dataset = new SourceDataset({
122    src: 'slice',
123    schema: {id: NUM, ts: LONG},
124  });
125
126  expect(dataset.implements({id: NUM})).toBe(true);
127  expect(dataset.implements({id: NUM, ts: LONG})).toBe(true);
128  expect(dataset.implements({id: NUM, ts: LONG, name: STR})).toBe(false);
129  expect(dataset.implements({id: LONG})).toBe(false);
130});
131
132test('implements with relaxed compat checks on optional types', () => {
133  expect(
134    new SourceDataset({
135      src: 'slice',
136      schema: {foo: NUM_NULL, bar: LONG_NULL, baz: STR_NULL, qux: BLOB_NULL},
137    }).implements({
138      foo: NUM_NULL,
139      bar: LONG_NULL,
140      baz: STR_NULL,
141      qux: BLOB_NULL,
142    }),
143  ).toBe(true);
144
145  expect(
146    new SourceDataset({
147      src: 'slice',
148      schema: {foo: NUM, bar: LONG, baz: STR, qux: BLOB},
149    }).implements({
150      foo: NUM_NULL,
151      bar: LONG_NULL,
152      baz: STR_NULL,
153      qux: BLOB_NULL,
154    }),
155  ).toBe(true);
156
157  expect(
158    new SourceDataset({
159      src: 'slice',
160      schema: {foo: NUM_NULL, bar: LONG_NULL, baz: STR_NULL, qux: BLOB_NULL},
161    }).implements({
162      foo: NUM,
163      bar: LONG,
164      baz: STR,
165      qux: BLOB,
166    }),
167  ).toBe(false);
168});
169
170test('find the schema of a simple dataset', () => {
171  const dataset = new SourceDataset({
172    src: 'slice',
173    schema: {id: NUM, ts: LONG},
174  });
175
176  expect(dataset.schema).toMatchObject({id: NUM, ts: LONG});
177});
178
179test('find the schema of a union where source sets differ in their names', () => {
180  const dataset = new UnionDataset([
181    new SourceDataset({
182      src: 'slice',
183      schema: {foo: NUM},
184    }),
185    new SourceDataset({
186      src: 'slice',
187      schema: {bar: NUM},
188    }),
189  ]);
190
191  expect(dataset.schema).toMatchObject({});
192});
193
194test('find the schema of a union with differing source sets', () => {
195  const dataset = new UnionDataset([
196    new SourceDataset({
197      src: 'slice',
198      schema: {foo: NUM},
199    }),
200    new SourceDataset({
201      src: 'slice',
202      schema: {foo: LONG},
203    }),
204  ]);
205
206  expect(dataset.schema).toMatchObject({});
207});
208
209test('find the schema of a union with one column in common', () => {
210  const dataset = new UnionDataset([
211    new SourceDataset({
212      src: 'slice',
213      schema: {foo: NUM, bar: NUM},
214    }),
215    new SourceDataset({
216      src: 'slice',
217      schema: {foo: NUM, baz: NUM},
218    }),
219  ]);
220
221  expect(dataset.schema).toMatchObject({foo: NUM});
222});
223
224test('optimize a union dataset', () => {
225  const dataset = new UnionDataset([
226    new SourceDataset({
227      src: 'slice',
228      schema: {},
229      filter: {
230        col: 'track_id',
231        eq: 123,
232      },
233    }),
234    new SourceDataset({
235      src: 'slice',
236      schema: {},
237      filter: {
238        col: 'track_id',
239        eq: 456,
240      },
241    }),
242  ]);
243
244  expect(dataset.optimize()).toEqual({
245    src: 'slice',
246    schema: {},
247    filter: {
248      col: 'track_id',
249      in: [123, 456],
250    },
251  });
252});
253
254test('optimize a union dataset with different types of filters', () => {
255  const dataset = new UnionDataset([
256    new SourceDataset({
257      src: 'slice',
258      schema: {},
259      filter: {
260        col: 'track_id',
261        eq: 123,
262      },
263    }),
264    new SourceDataset({
265      src: 'slice',
266      schema: {},
267      filter: {
268        col: 'track_id',
269        in: [456, 789],
270      },
271    }),
272  ]);
273
274  expect(dataset.optimize()).toEqual({
275    src: 'slice',
276    schema: {},
277    filter: {
278      col: 'track_id',
279      in: [123, 456, 789],
280    },
281  });
282});
283
284test('optimize a union dataset with different schemas', () => {
285  const dataset = new UnionDataset([
286    new SourceDataset({
287      src: 'slice',
288      schema: {foo: NUM},
289    }),
290    new SourceDataset({
291      src: 'slice',
292      schema: {bar: NUM},
293    }),
294  ]);
295
296  expect(dataset.optimize()).toEqual({
297    src: 'slice',
298    // The resultant schema is the combination of the union's member's schemas,
299    // as we know the source is the same as we know we can get all of the 'seen'
300    // columns from the source.
301    schema: {
302      foo: NUM,
303      bar: NUM,
304    },
305  });
306});
307
308test('union type widening', () => {
309  const dataset = new UnionDataset([
310    new SourceDataset({
311      src: 'slice',
312      schema: {foo: NUM, bar: STR_NULL, baz: BLOB, missing: UNKNOWN},
313    }),
314    new SourceDataset({
315      src: 'slice',
316      schema: {foo: NUM_NULL, bar: STR, baz: LONG},
317    }),
318  ]);
319
320  expect(dataset.schema).toEqual({
321    foo: NUM_NULL,
322    bar: STR_NULL,
323    baz: UNKNOWN,
324  });
325});
326