package com.thinkbiganalytics.util;
/*-
* #%L
* thinkbig-nifi-core-processors
* %%
* Copyright (C) 2017 ThinkBig Analytics
* %%
* 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.
* #L%
*/
import com.thinkbiganalytics.nifi.thrift.api.RowVisitor;
import org.apache.avro.Schema;
import org.apache.avro.SchemaBuilder;
import org.apache.avro.file.DataFileReader;
import org.apache.avro.file.SeekableByteArrayInput;
import org.apache.avro.file.SeekableInput;
import org.apache.avro.generic.GenericDatumReader;
import org.apache.avro.generic.GenericRecord;
import org.apache.avro.util.Utf8;
import org.junit.Assert;
import org.junit.Test;
import org.mockito.InOrder;
import org.mockito.Mockito;
import java.io.ByteArrayOutputStream;
import java.math.BigDecimal;
import java.nio.ByteBuffer;
import java.sql.Date;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.RowId;
import java.sql.SQLException;
import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Types;
import static org.junit.Assert.assertEquals;
public class JdbcCommonTest {
/**
* Verify converting results to avro.
*/
@Test
public void convertToAvroStream() throws Exception {
// Mock result set metadata
final ResultSetMetaData metadata = Mockito.mock(ResultSetMetaData.class);
Mockito.when(metadata.getColumnCount()).thenReturn(14);
Mockito.when(metadata.getColumnName(1)).thenReturn("event");
Mockito.when(metadata.getColumnName(2)).thenReturn("empty");
Mockito.when(metadata.getColumnName(3)).thenReturn("binary");
Mockito.when(metadata.getColumnName(4)).thenReturn("byte");
Mockito.when(metadata.getColumnName(5)).thenReturn("decimal");
Mockito.when(metadata.getColumnName(6)).thenReturn("maxlong");
Mockito.when(metadata.getColumnName(7)).thenReturn("date");
Mockito.when(metadata.getColumnName(8)).thenReturn("time");
Mockito.when(metadata.getColumnName(9)).thenReturn("timestamp");
Mockito.when(metadata.getColumnName(10)).thenReturn("bool");
Mockito.when(metadata.getColumnName(11)).thenReturn("int");
Mockito.when(metadata.getColumnName(12)).thenReturn("id");
Mockito.when(metadata.getColumnName(13)).thenReturn("float");
Mockito.when(metadata.getColumnName(14)).thenReturn("double");
Mockito.when(metadata.getColumnType(1)).thenReturn(Types.VARCHAR);
Mockito.when(metadata.getColumnType(2)).thenReturn(Types.VARCHAR);
Mockito.when(metadata.getColumnType(3)).thenReturn(Types.VARBINARY);
Mockito.when(metadata.getColumnType(4)).thenReturn(Types.TINYINT);
Mockito.when(metadata.getColumnType(5)).thenReturn(Types.DECIMAL);
Mockito.when(metadata.getColumnType(6)).thenReturn(Types.BIGINT);
Mockito.when(metadata.getColumnType(7)).thenReturn(Types.DATE);
Mockito.when(metadata.getColumnType(8)).thenReturn(Types.TIME);
Mockito.when(metadata.getColumnType(9)).thenReturn(Types.TIMESTAMP);
Mockito.when(metadata.getColumnType(10)).thenReturn(Types.BOOLEAN);
Mockito.when(metadata.getColumnType(11)).thenReturn(Types.INTEGER);
Mockito.when(metadata.getColumnType(12)).thenReturn(Types.ROWID);
Mockito.when(metadata.getColumnType(13)).thenReturn(Types.FLOAT);
Mockito.when(metadata.getColumnType(14)).thenReturn(Types.DOUBLE);
Mockito.when(metadata.getTableName(Mockito.anyInt())).thenReturn("mockito");
Mockito.when(metadata.isSigned(11)).thenReturn(true);
// Mock result set
final ResultSet results = Mockito.mock(ResultSet.class);
Mockito.when(results.getByte(4)).thenReturn((byte) 42);
Mockito.when(results.getBytes(3)).thenReturn(new byte[]{72, 73});
Mockito.when(results.getDate(7)).thenReturn(new Date(1483660800000L));
Mockito.when(results.getMetaData()).thenReturn(metadata);
Mockito.when(results.getObject(1)).thenReturn("Fun Friday");
Mockito.when(results.getObject(2)).thenReturn(null);
Mockito.when(results.getObject(3)).thenReturn(new byte[]{72, 73});
Mockito.when(results.getObject(4)).thenReturn((byte) 42);
Mockito.when(results.getObject(5)).thenReturn(new BigDecimal("3.14159265359"));
Mockito.when(results.getObject(6)).thenReturn(Long.MAX_VALUE);
Mockito.when(results.getObject(7)).thenReturn(new Date(1483660800000L));
Mockito.when(results.getObject(8)).thenReturn(new Time(42600000L));
Mockito.when(results.getObject(9)).thenReturn(new Timestamp(1483703400000L));
Mockito.when(results.getObject(10)).thenReturn(Boolean.TRUE);
Mockito.when(results.getObject(11)).thenReturn(12);
Mockito.when(results.getObject(12)).thenReturn((RowId) () -> new byte[]{1});
Mockito.when(results.getObject(13)).thenReturn(2.5f);
Mockito.when(results.getObject(14)).thenReturn(1.61803);
Mockito.when(results.getTime(8)).thenReturn(new Time(42600000L));
Mockito.when(results.getTimestamp(7)).thenThrow(SQLException.class);
Mockito.when(results.getTimestamp(9)).thenReturn(new Timestamp(1483703400000L));
Mockito.when(results.next()).thenReturn(true).thenReturn(false);
// Test converting to avro
final ByteArrayOutputStream out = new ByteArrayOutputStream();
final RowVisitor visitor = Mockito.mock(RowVisitor.class);
Schema avroSchema = JdbcCommon.createSchema(results);
JdbcCommon.convertToAvroStream(results, out, visitor, avroSchema);
final InOrder inOrder = Mockito.inOrder(visitor);
inOrder.verify(visitor).visitRow(results);
inOrder.verify(visitor).visitColumn("event", Types.VARCHAR, "Fun Friday");
inOrder.verify(visitor).visitColumn("empty", Types.VARCHAR, (String) null);
inOrder.verify(visitor).visitColumn(Mockito.eq("binary"), Mockito.eq(Types.VARBINARY), Mockito.anyString());
inOrder.verify(visitor).visitColumn("byte", Types.TINYINT, "42");
inOrder.verify(visitor).visitColumn("decimal", Types.DECIMAL, "3.14159265359");
inOrder.verify(visitor).visitColumn("maxlong", Types.BIGINT, Long.toString(Long.MAX_VALUE));
inOrder.verify(visitor).visitColumn("date", Types.DATE, new Date(1483660800000L));
inOrder.verify(visitor).visitColumn("time", Types.TIME, new Time(42600000L));
inOrder.verify(visitor).visitColumn("timestamp", Types.TIMESTAMP, new Timestamp(1483703400000L));
inOrder.verify(visitor).visitColumn("bool", Types.BOOLEAN, "true");
inOrder.verify(visitor).visitColumn("int", Types.INTEGER, "12");
inOrder.verify(visitor).visitColumn(Mockito.eq("id"), Mockito.eq(Types.ROWID), Mockito.anyString());
inOrder.verify(visitor).visitColumn("float", Types.FLOAT, "2.5");
inOrder.verify(visitor).visitColumn("double", Types.DOUBLE, "1.61803");
inOrder.verifyNoMoreInteractions();
final Schema schema = SchemaBuilder
.record("mockito")
.namespace("any.data")
.fields()
.name("event").type().nullable().stringType().noDefault()
.name("empty").type().nullable().stringType().noDefault()
.name("binary").type().nullable().bytesType().noDefault()
.name("byte").type().nullable().intType().noDefault()
.name("decimal").type().nullable().stringType().noDefault()
.name("maxlong").type().nullable().longType().noDefault()
.name("date").type().nullable().stringType().noDefault()
.name("time").type().nullable().stringType().noDefault()
.name("timestamp").type().nullable().stringType().noDefault()
.name("bool").type().nullable().booleanType().noDefault()
.name("int").type().nullable().intType().noDefault()
.name("id").type().nullable().stringType().noDefault()
.name("float").type().nullable().floatType().noDefault()
.name("double").type().nullable().doubleType().noDefault()
.endRecord();
final SeekableInput input = new SeekableByteArrayInput(out.toByteArray());
final GenericDatumReader<GenericRecord> datumReader = new GenericDatumReader<>(schema);
final DataFileReader<GenericRecord> dataReader = new DataFileReader<>(input, datumReader);
final GenericRecord record = dataReader.next();
assertEquals(new Utf8("Fun Friday"), record.get(0));
assertEquals(null, record.get(1));
assertEquals(ByteBuffer.wrap(new byte[]{72, 73}), record.get(2));
assertEquals(42, record.get(3));
assertEquals(new Utf8("3.14159265359"), record.get(4));
assertEquals(Long.MAX_VALUE, record.get(5));
assertEquals(new Utf8("2017-01-06T00:00:00.000Z"), record.get(6));
assertEquals(new Utf8("11:50:00.000Z"), record.get(7));
assertEquals(new Utf8("2017-01-06T11:50:00.000Z"), record.get(8));
assertEquals(Boolean.TRUE, record.get(9));
assertEquals(12, record.get(10));
Assert.assertNotNull(record.get(11));
assertEquals(2.5f, record.get(12));
assertEquals(1.61803, record.get(13));
Assert.assertFalse(dataReader.hasNext());
}
/**
* Verify converting results to delimited text.
*/
@Test
public void convertToDelimitedStream() throws Exception {
// Mock result set metadata
final ResultSetMetaData metadata = Mockito.mock(ResultSetMetaData.class);
Mockito.when(metadata.getColumnCount()).thenReturn(6);
Mockito.when(metadata.getColumnName(1)).thenReturn("event");
Mockito.when(metadata.getColumnName(2)).thenReturn("empty");
Mockito.when(metadata.getColumnName(3)).thenReturn("date");
Mockito.when(metadata.getColumnName(4)).thenReturn("time");
Mockito.when(metadata.getColumnName(5)).thenReturn("timestamp");
Mockito.when(metadata.getColumnName(6)).thenReturn("custom");
Mockito.when(metadata.getColumnType(1)).thenReturn(Types.VARCHAR);
Mockito.when(metadata.getColumnType(2)).thenReturn(Types.VARCHAR);
Mockito.when(metadata.getColumnType(3)).thenReturn(Types.DATE);
Mockito.when(metadata.getColumnType(4)).thenReturn(Types.TIME);
Mockito.when(metadata.getColumnType(5)).thenReturn(Types.TIMESTAMP);
Mockito.when(metadata.getColumnType(6)).thenReturn(Types.TIMESTAMP);
// Mock result set
final ResultSet results = Mockito.mock(ResultSet.class);
Mockito.when(results.getDate(3)).thenReturn(new Date(1483660800000L));
Mockito.when(results.getDate(6)).thenThrow(SQLException.class);
Mockito.when(results.getMetaData()).thenReturn(metadata);
Mockito.when(results.getString(1)).thenReturn("Fun Friday");
Mockito.when(results.getTime(4)).thenReturn(new Time(42600000L));
Mockito.when(results.getTimestamp(3)).thenThrow(SQLException.class);
Mockito.when(results.getTimestamp(5)).thenReturn(new Timestamp(1483703400000L));
Mockito.when(results.getTimestamp(6)).thenThrow(SQLException.class);
Mockito.when(results.next()).thenReturn(true).thenReturn(false);
// Test converting to delimited text
final ByteArrayOutputStream out = new ByteArrayOutputStream();
final RowVisitor visitor = Mockito.mock(RowVisitor.class);
JdbcCommon.convertToDelimitedStream(results, out, visitor, " ");
final InOrder inOrder = Mockito.inOrder(visitor);
inOrder.verify(visitor).visitRow(results);
inOrder.verify(visitor).visitColumn("event", Types.VARCHAR, "Fun Friday");
inOrder.verify(visitor).visitColumn("empty", Types.VARCHAR, (String) null);
inOrder.verify(visitor).visitColumn("date", Types.DATE, new Timestamp(1483660800000L));
inOrder.verify(visitor).visitColumn("time", Types.TIME, new Time(42600000L));
inOrder.verify(visitor).visitColumn("timestamp", Types.TIMESTAMP, new Timestamp(1483703400000L));
inOrder.verify(visitor).visitColumn("custom", Types.TIMESTAMP, (Timestamp) null);
inOrder.verifyNoMoreInteractions();
assertEquals("event empty date time timestamp custom\n\"Fun Friday\" 2017-01-06T00:00:00.000Z 11:50:00.000Z 2017-01-06T11:50:00.000Z \n", new String(out.toByteArray(), "UTF-8"));
}
/**
* Verify row count for a {@code null} result set.
*/
@Test
public void convertToDelimitedStreamWithNull() throws Exception {
final long count = JdbcCommon.convertToDelimitedStream(null, null, null, ",");
assertEquals(0L, count);
}
/**
* Verify schema format for setting up feed table
*/
@Test
public void getAvroSchemaForFeedSetupTest() {
final Schema schema = SchemaBuilder
.record("test_record")
.namespace("my_schema")
.fields()
.name("po_id").type().nullable().intType().intDefault(0)
.name("customer_name").type().nullable().stringType().stringDefault("null")
.name("total_amount").type().nullable().stringType().stringDefault("null")
.name("is_urgent").type().nullable().booleanType().booleanDefault(false)
.name("last_update_time").type().nullable().stringType().stringDefault("null")
.name("photo").type().nullable().bytesType().bytesDefault("null")
.name("identifier").type().nullable().longType().longDefault(0)
.name("state").type().stringType().stringDefault("null")
.endRecord();
String obtained = JdbcCommon.getAvroSchemaForFeedSetup(schema);
String expected = "po_id|int||0|0|0\n"
+ "customer_name|string||0|0|0\n"
+ "total_amount|string||0|0|0\n"
+ "is_urgent|boolean||0|0|0\n"
+ "last_update_time|string||0|0|0\n"
+ "photo|binary||0|0|0\n"
+ "identifier|bigint||0|0|0\n"
+ "state|string||0|0|0";
assertEquals("Avro schema not mapped correctly for feed table setup", expected, obtained);
}
}