// Copyright (C) 2020 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. import {RawQueryResult} from './protos'; import { findColumnIndex, iter, NUM, NUM_NULL, slowlyCountRows, STR, STR_NULL } from './query_iterator'; const COLUMN_TYPE_STR = RawQueryResult.ColumnDesc.Type.STRING; const COLUMN_TYPE_DOUBLE = RawQueryResult.ColumnDesc.Type.DOUBLE; const COLUMN_TYPE_LONG = RawQueryResult.ColumnDesc.Type.LONG; test('Columnar iteration slowlyCountRows', () => { const r = new RawQueryResult({ columnDescriptors: [{ name: 'string_column', type: COLUMN_TYPE_STR, }], numRecords: 1, columns: [{ stringValues: ['foo'], isNulls: [false], }], }); expect(slowlyCountRows(r)).toBe(1); }); test('Columnar iteration findColumnIndex', () => { const r = new RawQueryResult({ columnDescriptors: [ { name: 'strings', type: COLUMN_TYPE_STR, }, { name: 'doubles', type: COLUMN_TYPE_DOUBLE, }, { name: 'longs', type: COLUMN_TYPE_LONG, }, { name: 'nullable_strings', type: COLUMN_TYPE_STR, }, { name: 'nullable_doubles', type: COLUMN_TYPE_DOUBLE, }, { name: 'nullable_longs', type: COLUMN_TYPE_LONG, }, { name: 'twin', type: COLUMN_TYPE_LONG, }, { name: 'twin', type: COLUMN_TYPE_STR, } ], numRecords: 1, columns: [ { stringValues: ['foo'], isNulls: [false], }, { doubleValues: [1], isNulls: [false], }, { longValues: [1], isNulls: [false], }, { stringValues: [''], isNulls: [true], }, { doubleValues: [0], isNulls: [true], }, { longValues: [0], isNulls: [true], }, { doubleValues: [0], isNulls: [false], }, { stringValues: [''], isNulls: [false], } ], }); expect(findColumnIndex(r, 'strings', STR)).toBe(0); expect(findColumnIndex(r, 'doubles', NUM)).toBe(1); expect(findColumnIndex(r, 'longs', NUM)).toBe(2); expect(findColumnIndex(r, 'nullable_strings', STR_NULL)).toBe(3); expect(findColumnIndex(r, 'nullable_doubles', NUM_NULL)).toBe(4); expect(findColumnIndex(r, 'nullable_longs', NUM_NULL)).toBe(5); expect(() => findColumnIndex(r, 'no such col', NUM)).toThrow(Error); // It's allowable to expect nulls but for the whole column to be non-null... expect(findColumnIndex(r, 'strings', STR_NULL)).toBe(0); expect(findColumnIndex(r, 'doubles', NUM_NULL)).toBe(1); expect(findColumnIndex(r, 'longs', NUM_NULL)).toBe(2); // ...but if we expect no-nulls there shouldn't be even one: expect(() => findColumnIndex(r, 'nullable_strings', STR)).toThrow(Error); expect(() => findColumnIndex(r, 'nullable_doubles', NUM)).toThrow(Error); expect(() => findColumnIndex(r, 'nullable_longs', NUM)).toThrow(Error); // If multiple columns have the desired name we error even if we could // distinguish based on the type: expect(() => findColumnIndex(r, 'twin', NUM)).toThrow(Error); expect(() => findColumnIndex(r, 'strings', NUM)).toThrow(Error); expect(() => findColumnIndex(r, 'longs', STR)).toThrow(Error); expect(() => findColumnIndex(r, 'doubles', STR)).toThrow(Error); }); test('Columnar iteration over two rows', () => { const r = new RawQueryResult({ columnDescriptors: [{ name: 'name', type: COLUMN_TYPE_STR, }], numRecords: 2, columns: [{ stringValues: ['Alice', 'Bob'], isNulls: [false, false], }], }); const it = iter({'name': STR}, r); expect(it.valid()).toBe(true); const name: string = it.row.name; expect(name).toBe('Alice'); it.next(); expect(it.valid()).toBe(true); expect(it.row.name).toBe('Bob'); it.next(); expect(it.valid()).toBe(false); }); test('Columnar iteration over empty query set', () => { const r = new RawQueryResult({ columnDescriptors: [{ name: 'emptyColumn', type: COLUMN_TYPE_STR, }], numRecords: 0, columns: [{ stringValues: [], isNulls: [], }], }); { const it = iter({'emptyColumn': STR}, r); expect(it.valid()).toBe(false); } { const it = iter({'emptyColumn': NUM}, r); expect(it.valid()).toBe(false); } { const it = iter({'emptyColumn': NUM_NULL}, r); expect(it.valid()).toBe(false); } { const it = iter({'emptyColumn': STR_NULL}, r); expect(it.valid()).toBe(false); } }); test('Columnar iteration first row is null', () => { const r = new RawQueryResult({ columnDescriptors: [{ name: 'numbers', type: COLUMN_TYPE_STR, }], numRecords: 2, columns: [{ stringValues: ['[NULL]'], doubleValues: [0], longValues: [0, 1], isNulls: [true, false], }], }); const it = iter({'numbers': NUM_NULL}, r); expect(it.valid()).toBe(true); expect(it.row.numbers).toBe(null); it.next(); expect(it.valid()).toBe(true); expect(it.row.numbers).toBe(1); it.next(); expect(it.valid()).toBe(false); });