blob: 5ecaed1458be94995c2d7a00fbc05e6a37a75260 [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.store.http;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import org.apache.drill.common.logical.StoragePluginConfig.AuthMode;
import org.apache.drill.common.logical.security.PlainCredentialsProvider;
import org.apache.drill.common.types.TypeProtos.DataMode;
import org.apache.drill.common.types.TypeProtos.MinorType;
import org.apache.drill.common.util.DrillFileUtils;
import org.apache.drill.exec.physical.rowSet.RowSet;
import org.apache.drill.exec.physical.rowSet.RowSetBuilder;
import org.apache.drill.exec.record.metadata.ColumnMetadata;
import org.apache.drill.exec.record.metadata.MetadataUtils;
import org.apache.drill.exec.record.metadata.SchemaBuilder;
import org.apache.drill.exec.record.metadata.TupleMetadata;
import org.apache.drill.exec.store.security.UsernamePasswordCredentials;
import com.google.common.base.Charsets;
import com.google.common.collect.ImmutableMap;
import com.google.common.io.Files;
import org.apache.drill.test.ClusterFixture;
import org.apache.drill.test.ClusterTest;
import org.apache.drill.test.rowSet.RowSetUtilities;
import org.junit.BeforeClass;
import org.junit.Test;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import static org.apache.drill.test.rowSet.RowSetUtilities.strArray;
public class TestProvidedSchema extends ClusterTest {
private static final int MOCK_SERVER_PORT = 47777;
private static String TEST_JSON_PAGE1;
private static String TEST_SCHEMA_CHANGE1;
@BeforeClass
public static void setup() throws Exception {
startCluster(ClusterFixture.builder(dirTestWatcher));
TEST_JSON_PAGE1 = Files.asCharSource(DrillFileUtils.getResourceAsFile("/data/p1.json"), Charsets.UTF_8).read();
TEST_SCHEMA_CHANGE1 = Files.asCharSource(DrillFileUtils.getResourceAsFile("/data/schema_change_1.json"), Charsets.UTF_8).read();
dirTestWatcher.copyResourceToRoot(Paths.get("data/"));
makeMockConfig(cluster);
}
public static void makeMockConfig(ClusterFixture cluster) {
TupleMetadata simpleSchema = new SchemaBuilder()
.addNullable("col_1", MinorType.FLOAT8)
.addNullable("col_2", MinorType.FLOAT8)
.addNullable("col_3", MinorType.FLOAT8)
.build();
HttpJsonOptions jsonOptions = new HttpJsonOptions.HttpJsonOptionsBuilder()
.schema(simpleSchema)
.build();
HttpApiConfig basicJson = HttpApiConfig.builder()
.url(makeUrl("http://localhost:%d/json"))
.method("get")
.jsonOptions(jsonOptions)
.requireTail(false)
.inputType("json")
.build();
TupleMetadata mapSchema = new SchemaBuilder()
.addNullable("field1", MinorType.VARCHAR)
.addMap("field2")
.addNullable("nested_value1", MinorType.VARCHAR)
.addNullable("nested_value2", MinorType.VARCHAR)
.resumeSchema()
.buildSchema();
HttpJsonOptions jsonOptionsSchemaChange = new HttpJsonOptions.HttpJsonOptionsBuilder()
.schema(mapSchema)
.skipMalformedRecords(true)
.build();
HttpApiConfig schemaChange = HttpApiConfig.builder()
.url(makeUrl("http://localhost:%d/json"))
.method("get")
.jsonOptions(jsonOptionsSchemaChange)
.requireTail(false)
.inputType("json")
.build();
TupleMetadata partialMapSchema = new SchemaBuilder()
.addNullable("field1", MinorType.VARCHAR)
.addMap("field2")
.addNullable("nested_value1", MinorType.VARCHAR)
.resumeSchema()
.buildSchema();
HttpApiConfig partialSchema = HttpApiConfig.builder()
.url(makeUrl("http://localhost:%d/json"))
.method("get")
.jsonOptions(HttpJsonOptions.builder().schema(partialMapSchema).build())
.requireTail(false)
.inputType("json")
.build();
ColumnMetadata jsonColumn = MetadataUtils.newScalar("field2", MinorType.VARCHAR, DataMode.OPTIONAL);
jsonColumn.setProperty("drill.json-mode", "json");
TupleMetadata jsonModeSchema = new SchemaBuilder()
.addNullable("field1", MinorType.VARCHAR)
.add(jsonColumn)
.build();
HttpJsonOptions jsonModeOptions = HttpJsonOptions.builder()
.schema(jsonModeSchema)
.skipMalformedRecords(true)
.build();
HttpApiConfig jsonModeConfig = HttpApiConfig.builder()
.url(makeUrl("http://localhost:%d/json"))
.method("get")
.jsonOptions(jsonModeOptions)
.requireTail(false)
.inputType("json")
.build();
HttpApiConfig noSchema = HttpApiConfig.builder()
.url(makeUrl("http://localhost:%d/json"))
.method("get")
.requireTail(false)
.inputType("json")
.build();
Map<String, HttpApiConfig> configs = new HashMap<>();
configs.put("basicJson", basicJson);
configs.put("schemaChange", schemaChange);
configs.put("partialSchema", partialSchema);
configs.put("jsonMode", jsonModeConfig);
configs.put("noSchema", noSchema);
HttpStoragePluginConfig mockStorageConfigWithWorkspace =
new HttpStoragePluginConfig(false, false, configs, 2, 1000, "globaluser", "globalpass", "",
80, "", "", "", null, new PlainCredentialsProvider(ImmutableMap.of(
UsernamePasswordCredentials.USERNAME, "globaluser",
UsernamePasswordCredentials.PASSWORD, "globalpass")), AuthMode.SHARED_USER.name());
mockStorageConfigWithWorkspace.setEnabled(true);
cluster.defineStoragePlugin("local", mockStorageConfigWithWorkspace);
}
@Test
public void testProvidedSchema() throws Exception {
String sql = "SELECT * FROM `local`.`basicJson`";
try (MockWebServer server = startServer()) {
server.enqueue(new MockResponse().setResponseCode(200).setBody(TEST_JSON_PAGE1));
RowSet results = client.queryBuilder().sql(sql).rowSet();
TupleMetadata expectedSchema = new SchemaBuilder()
.addNullable("col_1", MinorType.FLOAT8)
.addNullable("col_2", MinorType.FLOAT8)
.addNullable("col_3", MinorType.FLOAT8)
.build();
RowSet expected = new RowSetBuilder(client.allocator(), expectedSchema)
.addRow(1.0, 2.0, 3.0)
.addRow(4.0, 5.0, 6.0)
.build();
RowSetUtilities.verify(expected, results);
}
}
@Test
public void testSchemaChangeWithProvidedSchema() throws Exception {
String sql = "SELECT * FROM `local`.`schemaChange`";
try (MockWebServer server = startServer()) {
server.enqueue(new MockResponse().setResponseCode(200).setBody(TEST_SCHEMA_CHANGE1));
RowSet results = client.queryBuilder().sql(sql).rowSet();
TupleMetadata expectedSchema = new SchemaBuilder()
.addNullable("field1", MinorType.VARCHAR)
.addMap("field2")
.addNullable("nested_value1", MinorType.VARCHAR)
.addNullable("nested_value2", MinorType.VARCHAR)
.resumeSchema()
.build();
RowSet expected = new RowSetBuilder(client.allocator(), expectedSchema)
.addRow("value1", strArray(null, null))
.addRow("value3", strArray("nv1", "nv2"))
.addRow("value5", strArray("nv3", "nv4"))
.build();
RowSetUtilities.verify(expected, results);
}
}
@Test
public void testPartialSchema() throws Exception {
String sql = "SELECT * FROM `local`.`partialSchema`";
try (MockWebServer server = startServer()) {
server.enqueue(new MockResponse().setResponseCode(200).setBody(TEST_SCHEMA_CHANGE1));
RowSet results = client.queryBuilder().sql(sql).rowSet();
TupleMetadata expectedSchema = new SchemaBuilder()
.addNullable("field1", MinorType.VARCHAR)
.addMap("field2")
.addNullable("nested_value1", MinorType.VARCHAR)
.addNullable("nested_value2", MinorType.VARCHAR)
.resumeSchema()
.build();
RowSet expected = new RowSetBuilder(client.allocator(), expectedSchema)
.addRow("value1", strArray(null, null))
.addRow("value3", strArray("nv1", "nv2"))
.addRow("value5", strArray("nv3", "nv4"))
.build();
RowSetUtilities.verify(expected, results);
}
}
@Test
public void testInlineSchema() throws Exception {
String sql = "SELECT * FROM table(`local`.`noSchema` " +
"(schema => 'inline=(`field1` VARCHAR, `field2` VARCHAR properties {`drill.json-mode` = `json`})'" +
"))";
try (MockWebServer server = startServer()) {
server.enqueue(new MockResponse().setResponseCode(200).setBody(TEST_SCHEMA_CHANGE1));
RowSet results = client.queryBuilder().sql(sql).rowSet();
TupleMetadata expectedSchema = new SchemaBuilder()
.addNullable("field1", MinorType.VARCHAR)
.addNullable("field2", MinorType.VARCHAR)
.build();
RowSet expected = new RowSetBuilder(client.allocator(), expectedSchema)
.addRow("value1", "value2")
.addRow("value3", "{\"nested_value1\": nv1, \"nested_value2\": nv2}")
.addRow("value5", "{\"nested_value1\": nv3, \"nested_value2\": nv4}")
.build();
RowSetUtilities.verify(expected, results);
}
}
@Test
public void testPartialJSONSchema() throws Exception {
String sql = "SELECT * FROM `local`.`partialSchema`";
try (MockWebServer server = startServer()) {
server.enqueue(new MockResponse().setResponseCode(200).setBody(TEST_SCHEMA_CHANGE1));
RowSet results = client.queryBuilder().sql(sql).rowSet();
TupleMetadata expectedSchema = new SchemaBuilder()
.addNullable("field1", MinorType.VARCHAR)
.addMap("field2")
.addNullable("nested_value1", MinorType.VARCHAR)
.addNullable("nested_value2", MinorType.VARCHAR)
.resumeSchema()
.build();
RowSet expected = new RowSetBuilder(client.allocator(), expectedSchema)
.addRow("value1", strArray(null, null))
.addRow("value3", strArray("nv1", "nv2"))
.addRow("value5", strArray("nv3", "nv4"))
.build();
RowSetUtilities.verify(expected, results);
}
}
@Test
public void testJsonMode() throws Exception {
String sql = "SELECT * FROM `local`.`jsonMode`";
try (MockWebServer server = startServer()) {
server.enqueue(new MockResponse().setResponseCode(200).setBody(TEST_SCHEMA_CHANGE1));
RowSet results = client.queryBuilder().sql(sql).rowSet();
TupleMetadata expectedSchema = new SchemaBuilder()
.addNullable("field1", MinorType.VARCHAR)
.addNullable("field2", MinorType.VARCHAR)
.build();
RowSet expected = new RowSetBuilder(client.allocator(), expectedSchema)
.addRow("value1", "value2")
.addRow("value3", "{\"nested_value1\": nv1, \"nested_value2\": nv2}")
.addRow("value5", "{\"nested_value1\": nv3, \"nested_value2\": nv4}")
.build();
RowSetUtilities.verify(expected, results);
}
}
/**
* Helper function to start the MockHTTPServer
* @return Started Mock server
* @throws IOException If the server cannot start, throws IOException
*/
private MockWebServer startServer() throws IOException {
MockWebServer server = new MockWebServer();
server.start(MOCK_SERVER_PORT);
return server;
}
private static String makeUrl(String url) {
return String.format(url, MOCK_SERVER_PORT);
}
}