blob: b4700a6f1a3d23d391e69fc32a50bbc43dca495d [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
package org.apache.drill.exec.physical.resultSet.project;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.drill.categories.RowSetTests;
import org.apache.drill.common.exceptions.UserException;
import org.apache.drill.common.expression.SchemaPath;
import org.apache.drill.exec.physical.resultSet.impl.RowSetTestUtils;
import org.apache.drill.exec.physical.resultSet.project.RequestedTuple.RequestedColumn;
import org.apache.drill.exec.physical.resultSet.project.RequestedTuple.TupleProjectionType;
import org.apache.drill.test.BaseTest;
import org.junit.Test;
import org.junit.experimental.categories.Category;
/**
* Test the projection list parser: parses a list of SchemaPath
* items into a detailed structure, handling duplicate or overlapping
* items. Special cases the select-all (SELECT *) and select none
* (SELECT COUNT(*)) cases.
* <p>
* These tests should verify everything about (runtime) projection
* parsing; the only bits not tested here is that which is
* inherently specific to some use case.
*/
@Category(RowSetTests.class)
public class TestProjectedTuple extends BaseTest {
@Test
public void testProjectionAll() {
// Null map means everything is projected
RequestedTuple projSet = RequestedTupleImpl.parse(null);
assertEquals(TupleProjectionType.ALL, projSet.type());
// Not defined well; the tuple contains a wildcard
// assertEquals(ProjectionType.GENERAL, projSet.projectionType("foo"));
projSet = ImpliedTupleRequest.ALL_MEMBERS;
assertTrue(projSet instanceof ImpliedTupleRequest);
assertEquals(ProjectionType.GENERAL, projSet.projectionType("foo"));
}
/**
* Test an empty projection which occurs in a
* SELECT COUNT(*) query.
*/
@Test
public void testProjectionNone() {
// Empty list means nothing is projected
RequestedTuple projSet = RequestedTupleImpl.parse(Collections.emptyList());
assertEquals(TupleProjectionType.NONE, projSet.type());
assertTrue(projSet instanceof ImpliedTupleRequest);
List<RequestedColumn> cols = projSet.projections();
assertEquals(0, cols.size());
assertEquals(ProjectionType.UNPROJECTED, projSet.projectionType("foo"));
}
@Test
public void testProjectionSimple() {
// Simple non-map columns
RequestedTuple projSet = RequestedTupleImpl.parse(
RowSetTestUtils.projectList("a", "b", "c"));
assertTrue(projSet instanceof RequestedTupleImpl);
assertEquals(ProjectionType.GENERAL, projSet.projectionType("a"));
assertEquals(ProjectionType.GENERAL, projSet.projectionType("b"));
assertEquals(ProjectionType.UNPROJECTED, projSet.projectionType("d"));
List<RequestedColumn> cols = projSet.projections();
assertEquals(3, cols.size());
RequestedColumn a = cols.get(0);
assertEquals("a", a.name());
assertEquals(ProjectionType.GENERAL, a.type());
assertTrue(a.isSimple());
assertFalse(a.isWildcard());
// We don't know if a is a map or not (the simple term "a" under-determines
// the column type.) In case it is a map, we assume all of the map is
// projected.
assertNotNull(a.mapProjection());
assertEquals(TupleProjectionType.ALL, a.mapProjection().type());
assertNull(a.indexes());
assertEquals("b", cols.get(1).name());
assertEquals(ProjectionType.GENERAL, cols.get(1).type());
assertTrue(cols.get(1).isSimple());
assertEquals("c", cols.get(2).name());
assertEquals(ProjectionType.GENERAL, cols.get(2).type());
assertTrue(cols.get(2).isSimple());
}
@Test
public void testProjectionWholeMap() {
// Whole-map projection (note, fully projected maps are
// identical to projected simple columns at this level of
// abstraction.)
List<SchemaPath> projCols = new ArrayList<>();
projCols.add(SchemaPath.getSimplePath("map"));
RequestedTuple projSet = RequestedTupleImpl.parse(projCols);
assertTrue(projSet instanceof RequestedTupleImpl);
assertEquals(ProjectionType.GENERAL, projSet.projectionType("map"));
assertEquals(ProjectionType.UNPROJECTED, projSet.projectionType("another"));
RequestedTuple mapProj = projSet.mapProjection("map");
assertNotNull(mapProj);
assertTrue(mapProj instanceof ImpliedTupleRequest);
assertEquals(ProjectionType.GENERAL, mapProj.projectionType("foo"));
assertNotNull(projSet.mapProjection("another"));
assertEquals(ProjectionType.UNPROJECTED, projSet.mapProjection("another").projectionType("anyCol"));
}
@Test
public void testProjectionMapSubset() {
// Selected map projection, multiple levels, full projection
// at leaf level.
List<SchemaPath> projCols = new ArrayList<>();
projCols.add(SchemaPath.getCompoundPath("map", "a"));
projCols.add(SchemaPath.getCompoundPath("map", "b"));
projCols.add(SchemaPath.getCompoundPath("map", "map2", "x"));
RequestedTuple projSet = RequestedTupleImpl.parse(projCols);
assertTrue(projSet instanceof RequestedTupleImpl);
assertEquals(ProjectionType.TUPLE, projSet.projectionType("map"));
// Map: an explicit map at top level
RequestedTuple mapProj = projSet.mapProjection("map");
assertTrue(mapProj instanceof RequestedTupleImpl);
assertEquals(ProjectionType.GENERAL, mapProj.projectionType("a"));
assertEquals(ProjectionType.GENERAL, mapProj.projectionType("b"));
assertEquals(ProjectionType.TUPLE, mapProj.projectionType("map2"));
assertEquals(ProjectionType.UNPROJECTED, mapProj.projectionType("bogus"));
// Map b: an implied nested map
RequestedTuple bMapProj = mapProj.mapProjection("b");
assertNotNull(bMapProj);
assertTrue(bMapProj instanceof ImpliedTupleRequest);
assertEquals(ProjectionType.GENERAL, bMapProj.projectionType("foo"));
// Map2, an nested map, has an explicit projection
RequestedTuple map2Proj = mapProj.mapProjection("map2");
assertNotNull(map2Proj);
assertTrue(map2Proj instanceof RequestedTupleImpl);
assertEquals(ProjectionType.GENERAL, map2Proj.projectionType("x"));
assertEquals(ProjectionType.UNPROJECTED, map2Proj.projectionType("bogus"));
}
@Test
public void testProjectionMapFieldAndMap() {
// Project both a map member and the entire map.
{
List<SchemaPath> projCols = new ArrayList<>();
projCols.add(SchemaPath.getCompoundPath("map", "a"));
projCols.add(SchemaPath.getCompoundPath("map"));
RequestedTuple projSet = RequestedTupleImpl.parse(projCols);
assertTrue(projSet instanceof RequestedTupleImpl);
assertEquals(ProjectionType.TUPLE, projSet.projectionType("map"));
RequestedTuple mapProj = projSet.mapProjection("map");
assertTrue(mapProj instanceof ImpliedTupleRequest);
assertEquals(ProjectionType.GENERAL, mapProj.projectionType("a"));
// Didn't ask for b, but did ask for whole map.
assertEquals(ProjectionType.GENERAL, mapProj.projectionType("b"));
}
// Now the other way around.
{
List<SchemaPath> projCols = new ArrayList<>();
projCols.add(SchemaPath.getCompoundPath("map"));
projCols.add(SchemaPath.getCompoundPath("map", "a"));
RequestedTuple projSet = RequestedTupleImpl.parse(projCols);
assertTrue(projSet instanceof RequestedTupleImpl);
assertEquals(ProjectionType.TUPLE, projSet.projectionType("map"));
RequestedTuple mapProj = projSet.mapProjection("map");
assertTrue(mapProj instanceof ImpliedTupleRequest);
assertEquals(ProjectionType.GENERAL, mapProj.projectionType("a"));
assertEquals(ProjectionType.GENERAL, mapProj.projectionType("b"));
}
}
@Test
public void testMapDetails() {
RequestedTuple projSet = RequestedTupleImpl.parse(
RowSetTestUtils.projectList("a.b.c", "a.c", "d"));
List<RequestedColumn> cols = projSet.projections();
assertEquals(2, cols.size());
RequestedColumn a = cols.get(0);
assertEquals("a", a.name());
assertFalse(a.isSimple());
assertFalse(a.isArray());
assertTrue(a.isTuple());
{
assertNotNull(a.mapProjection());
List<RequestedColumn> aMembers = a.mapProjection().projections();
assertEquals(2, aMembers.size());
RequestedColumn a_b = aMembers.get(0);
assertEquals("b", a_b.name());
assertTrue(a_b.isTuple());
{
assertNotNull(a_b.mapProjection());
List<RequestedColumn> a_bMembers = a_b.mapProjection().projections();
assertEquals(1, a_bMembers.size());
assertEquals("c", a_bMembers.get(0).name());
assertTrue(a_bMembers.get(0).isSimple());
}
assertEquals("c", aMembers.get(1).name());
assertTrue(aMembers.get(1).isSimple());
}
assertEquals("d", cols.get(1).name());
assertTrue(cols.get(1).isSimple());
}
@Test
public void testMapDups() {
try {
RequestedTupleImpl.parse(
RowSetTestUtils.projectList("a.b", "a.c", "a.b"));
fail();
} catch (UserException e) {
// Expected
}
}
/**
* When the project list includes references to both the
* map as a whole, and members, then the parser is forgiving
* of duplicate map members since all members are projected.
*/
@Test
public void testMapDupsIgnored() {
RequestedTuple projSet = RequestedTupleImpl.parse(
RowSetTestUtils.projectList("a", "a.b", "a.c", "a.b"));
List<RequestedColumn> cols = projSet.projections();
assertEquals(1, cols.size());
}
@Test
public void testWildcard() {
RequestedTuple projSet = RequestedTupleImpl.parse(
RowSetTestUtils.projectList(SchemaPath.DYNAMIC_STAR));
List<RequestedColumn> cols = projSet.projections();
assertEquals(1, cols.size());
RequestedColumn wildcard = cols.get(0);
assertEquals(ProjectionType.WILDCARD, wildcard.type());
assertEquals(SchemaPath.DYNAMIC_STAR, wildcard.name());
assertFalse(wildcard.isSimple());
assertTrue(wildcard.isWildcard());
assertNull(wildcard.mapProjection());
assertNull(wildcard.indexes());
}
@Test
public void testSimpleDups() {
try {
RequestedTupleImpl.parse(RowSetTestUtils.projectList("a", "b", "a"));
fail();
} catch (UserException e) {
// Expected
}
}
@Test
public void testArray() {
RequestedTuple projSet = RequestedTupleImpl.parse(
RowSetTestUtils.projectList("a[1]", "a[3]"));
List<RequestedColumn> cols = projSet.projections();
assertEquals(1, cols.size());
assertEquals(ProjectionType.ARRAY, projSet.projectionType("a"));
RequestedColumn a = cols.get(0);
assertEquals("a", a.name());
assertTrue(a.isArray());
assertFalse(a.isSimple());
assertFalse(a.isTuple());
boolean[] indexes = a.indexes();
assertNotNull(indexes);
assertEquals(4, indexes.length);
assertFalse(indexes[0]);
assertTrue(indexes[1]);
assertFalse(indexes[2]);
assertTrue(indexes[3]);
}
/**
* Duplicate array entries are allowed to handle the
* use case of a[1], a[1].z. Each element is reported once;
* the project operator will create copies as needed.
*/
@Test
public void testArrayDups() {
RequestedTuple projSet = RequestedTupleImpl.parse(
RowSetTestUtils.projectList("a[1]", "a[3]", "a[1]", "a[3].z"));
List<RequestedColumn> cols = projSet.projections();
assertEquals(1, cols.size());
RequestedColumn a = cols.get(0);
assertEquals("a", a.name());
assertTrue(a.isArray());
boolean[] indexes = a.indexes();
assertNotNull(indexes);
assertEquals(4, indexes.length);
assertFalse(indexes[0]);
assertTrue(indexes[1]);
assertFalse(indexes[2]);
assertTrue(indexes[3]);
}
@Test
public void testArrayAndSimple() {
RequestedTuple projSet = RequestedTupleImpl.parse(
RowSetTestUtils.projectList("a[1]", "a"));
List<RequestedColumn> cols = projSet.projections();
assertEquals(1, cols.size());
RequestedColumn a = cols.get(0);
assertEquals("a", a.name());
assertTrue(a.isArray());
assertNull(a.indexes());
}
@Test
public void testSimpleAndArray() {
RequestedTuple projSet = RequestedTupleImpl.parse(
RowSetTestUtils.projectList("a", "a[1]"));
List<RequestedColumn> cols = projSet.projections();
assertEquals(1, cols.size());
RequestedColumn a = cols.get(0);
assertEquals("a", a.name());
assertTrue(a.isArray());
assertNull(a.indexes());
assertEquals(ProjectionType.ARRAY, projSet.projectionType("a"));
assertEquals(ProjectionType.UNPROJECTED, projSet.projectionType("foo"));
}
@Test
// Drill syntax does not support map arrays
public void testMapArray() {
RequestedTuple projSet = RequestedTupleImpl.parse(
RowSetTestUtils.projectList("a[1].x"));
List<RequestedColumn> cols = projSet.projections();
assertEquals(1, cols.size());
assertEquals(ProjectionType.TUPLE_ARRAY, cols.get(0).type());
assertEquals(ProjectionType.TUPLE_ARRAY, projSet.projectionType("a"));
}
}