blob: 3acc0b2ebd709d4b8b23cddef2da0d03b973dc54 [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.sql;
import org.apache.drill.PlanTestBase;
import com.google.common.base.Strings;
import org.apache.drill.test.TestBuilder;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Base class for view tests. It has utility methods which can be used when writing tests for views on tables
* in different storage engines such as Hive, HBase etc.
*/
public class TestBaseViewSupport extends PlanTestBase {
private static AtomicInteger viewSeqNum = new AtomicInteger(0);
/**
* Create view with given parameters.
*
* Current default schema "dfs"
*
* CREATE VIEW tmp.viewName(f1, f2) AS SELECT * FROM cp.`region.json`
*
* For the above CREATE VIEW query, function parameters are:
* viewSchema = "tmp"
* viewName = "viewName"
* finalSchema = "dfs.tmp"
* viewFields = "(f1, f2)"
* viewDef = "SELECT * FROM cp.`region.json`"
*
* @param viewSchema Schema name to prefix when referring to the view.
* @param viewName Name of the view.
* @param finalSchema Absolute schema path where the view is created. Pameter <i>viewSchema</i> may refer the schema
* with respect the current default schema. Combining <i>viewSchema</i> with default schema
* gives the final schema.
* @param viewFields If the created view needs to specify the fields, otherwise null
* @param viewDef Definition of the view.
* @throws Exception
*/
protected static void createViewHelper(final String viewSchema, final String viewName,
final String finalSchema, final String viewFields, final String viewDef) throws Exception {
String viewFullName = "`" + viewName + "`";
if (!Strings.isNullOrEmpty(viewSchema)) {
viewFullName = viewSchema + "." + viewFullName;
}
final String createViewSql = String.format("CREATE VIEW %s %s AS %s", viewFullName,
viewFields == null ? "" : viewFields, viewDef);
testBuilder()
.sqlQuery(createViewSql)
.unOrdered()
.baselineColumns("ok", "summary")
.baselineValues(true, String.format("View '%s' created successfully in '%s' schema", viewName, finalSchema))
.go();
}
/**
* Drop view with given parameters.
*
* Current schema "dfs"
* DROP VIEW tmp.viewName
*
* For the above DROP VIEW query, function parameters values are:
* viewSchema = "tmp"
* "viewName" = "viewName"
* "finalSchema" = "dfs.tmp"
*
* @param viewSchema
* @param viewName
* @param finalSchema
* @throws Exception
*/
protected static void dropViewHelper(final String viewSchema, final String viewName, final String finalSchema) throws
Exception{
String viewFullName = "`" + viewName + "`";
if (!Strings.isNullOrEmpty(viewSchema)) {
viewFullName = viewSchema + "." + viewFullName;
}
testBuilder()
.sqlQuery(String.format("DROP VIEW %s", viewFullName))
.unOrdered()
.baselineColumns("ok", "summary")
.baselineValues(true, String.format("View [%s] deleted successfully from schema [%s].", viewName, finalSchema))
.go();
}
/**
* Drop view if exists with given parameters.
*
* Current schema "dfs"
* DROP VIEW IF EXISTS tmp.viewName
*
* For the above DROP VIEW IF EXISTS query, function parameters values are:
* viewSchema = "tmp"
* "viewName" = "viewName"
* "finalSchema" = "dfs.tmp"
* "ifViewExists" = null
*
* @param viewSchema
* @param viewName
* @param finalSchema
* @param ifViewExists Helps to check query result depending from the existing of the view.
* @throws Exception
*/
protected static void dropViewIfExistsHelper(final String viewSchema, final String viewName, final String finalSchema, Boolean ifViewExists) throws
Exception{
String viewFullName = "`" + viewName + "`";
if (!Strings.isNullOrEmpty(viewSchema)) {
viewFullName = viewSchema + "." + viewFullName;
}
if (ifViewExists == null) {
// ifViewExists == null: we do not know whether the table exists. Just drop it if exists or skip dropping if doesn't exist
test(String.format("DROP VIEW IF EXISTS %s", viewFullName));
} else if (ifViewExists) {
testBuilder()
.sqlQuery(String.format("DROP VIEW IF EXISTS %s", viewFullName))
.unOrdered()
.baselineColumns("ok", "summary")
.baselineValues(true, String.format("View [%s] deleted successfully from schema [%s].", viewName, finalSchema))
.go();
} else {
testBuilder()
.sqlQuery(String.format("DROP VIEW IF EXISTS %s", viewFullName))
.unOrdered()
.baselineColumns("ok", "summary")
.baselineValues(false, String.format("View [%s] not found in schema [%s].", viewName, finalSchema))
.go();
}
}
/**
* Execute the given query and check against the given baseline.
*
* @param query
* @param baselineColumns
* @param baselineValues
* @throws Exception
*/
protected static void queryViewHelper(final String query, final String[] baselineColumns,
final List<Object[]> baselineValues) throws Exception {
TestBuilder testBuilder = testBuilder()
.sqlQuery(query)
.unOrdered()
.baselineColumns(baselineColumns);
for(Object[] values : baselineValues) {
testBuilder = testBuilder.baselineValues(values);
}
testBuilder.go();
}
/**
* Generate a view name by appending the test class with a increasing counter.
*
* @return
*/
protected static String generateViewName() {
return TestBaseViewSupport.class.getSimpleName() + "_" + viewSeqNum.incrementAndGet();
}
/**
* Tests creating a view with given parameters, query the view and check against the provided baselines and finally
* drop the view.
*
* @param finalSchema Absolute schema path where the view is going to be created.
* @param viewFields If view has any field list
* @param viewDef Definition of the view.
* @param queryOnView Query to run on created view. Refer to test view using "TEST_VIEW_NAME". Similarly schema
* using "TEST_SCHEMA".
* @param expectedBaselineColumns Expected columns from querying the view.
* @param expectedBaselineValues Expected row values from querying the view.
* @throws Exception
*/
protected static void testViewHelper(final String finalSchema, final String viewFields, final String viewDef,
String queryOnView, final String[] expectedBaselineColumns, final List<Object[]> expectedBaselineValues)
throws Exception {
final String viewName = generateViewName();
try {
createViewHelper(finalSchema, viewName, finalSchema, viewFields, viewDef);
queryOnView = queryOnView
.replace("TEST_VIEW_NAME", "`" + viewName + "`")
.replace("TEST_SCHEMA", finalSchema);
queryViewHelper(queryOnView, expectedBaselineColumns, expectedBaselineValues);
} finally {
dropViewHelper(finalSchema, viewName, finalSchema);
}
}
/**
* Convenient method for defining baselineColumns to be passed into
* view helper methods.
*
* @param names column names varargs
* @return column names array
*/
protected static String[] baselineColumns(String... names) {
return names;
}
/**
* Convenient method for grouping of expected rows into
* list of Object arrays, where each array represents concrete
* row. This method is used for defining baselineValues and passing it to
* view helper methods.
*
* @param rows rows in form of Object[] varargs
* @return list of rows
*/
protected static List<Object[]> baselineRows(Object[]... rows) {
return Collections.unmodifiableList(Arrays.asList(rows));
}
/**
* Helper method for conversion of Object varargs into
* array of objects. Used for passing rows into
* {@link TestBaseViewSupport#baselineRows(Object[]...)}
*
* @param columns Object varargs
* @return array of passed objects
*/
protected static Object[] row(Object... columns) {
return columns;
}
}