/*! ******************************************************************************
*
* Pentaho Data Integration
*
* Copyright (C) 2002-2017 by Pentaho : http://www.pentaho.com
*
*******************************************************************************
*
* 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.
*
******************************************************************************/
package org.pentaho.di.core.database;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.pentaho.di.core.RowMetaAndData;
import org.pentaho.di.core.exception.KettlePluginException;
import org.pentaho.di.core.plugins.DatabasePluginType;
import org.pentaho.di.core.row.RowMeta;
import org.pentaho.di.core.row.RowMetaInterface;
import org.pentaho.di.core.row.ValueMetaInterface;
import org.pentaho.di.core.row.value.ValueMetaNone;
import org.pentaho.di.core.row.value.ValueMetaPluginType;
import org.pentaho.di.core.KettleClientEnvironment;
import org.pentaho.di.core.exception.KettleException;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
public class DatabaseMetaTest {
private static final String TABLE_NAME = "tableName";
private static final String DROP_STATEMENT = "dropStatement";
private static final String DROP_STATEMENT_FALLBACK = "DROP TABLE IF EXISTS " + TABLE_NAME;
private DatabaseMeta databaseMeta;
private DatabaseInterface databaseInterface;
@BeforeClass
public static void setUpOnce() throws KettlePluginException, KettleException {
// Register Natives to create a default DatabaseMeta
DatabasePluginType.getInstance().searchPlugins();
ValueMetaPluginType.getInstance().searchPlugins();
KettleClientEnvironment.init();
}
@Before
public void setUp() {
databaseMeta = new DatabaseMeta();
databaseInterface = mock( DatabaseInterface.class );
databaseMeta.setDatabaseInterface( databaseInterface );
}
@Test
public void testGetDatabaseInterfacesMapWontReturnNullIfCalledSimultaneouslyWithClear() throws InterruptedException, ExecutionException {
final AtomicBoolean done = new AtomicBoolean( false );
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.submit( new Runnable() {
@Override
public void run() {
while ( !done.get() ) {
DatabaseMeta.clearDatabaseInterfacesMap();
}
}
} );
Future<Exception> getFuture = executorService.submit( new Callable<Exception>() {
@Override
public Exception call() throws Exception {
int i = 0;
while ( !done.get() ) {
assertNotNull( "Got null on try: " + i++, DatabaseMeta.getDatabaseInterfacesMap() );
if ( i > 30000 ) {
done.set( true );
}
}
return null;
}
} );
getFuture.get();
}
@Test
public void testDatabaseAccessTypeCode() throws Exception {
String expectedJndi = "JNDI";
String access = DatabaseMeta.getAccessTypeDesc( DatabaseMeta.getAccessType( expectedJndi ) );
assertEquals( expectedJndi, access );
}
@Test
public void testApplyingDefaultOptions() throws Exception {
HashMap<String, String> existingOptions = new HashMap<String, String>();
existingOptions.put( "type1.extra", "extraValue" );
existingOptions.put( "type1.existing", "existingValue" );
existingOptions.put( "type2.extra", "extraValue2" );
HashMap<String, String> newOptions = new HashMap<String, String>();
newOptions.put( "type1.new", "newValue" );
newOptions.put( "type1.existing", "existingDefault" );
when( databaseInterface.getExtraOptions() ).thenReturn( existingOptions );
when( databaseInterface.getDefaultOptions() ).thenReturn( newOptions );
databaseMeta.applyDefaultOptions( databaseInterface );
verify( databaseInterface ).addExtraOption( "type1", "new", "newValue" );
verify( databaseInterface, never() ).addExtraOption( "type1", "existing", "existingDefault" );
}
@Test
public void testQuoteReservedWords() {
DatabaseMeta databaseMeta = mock( DatabaseMeta.class );
doCallRealMethod().when( databaseMeta ).quoteReservedWords( any( RowMetaInterface.class ) );
doCallRealMethod().when( databaseMeta ).quoteField( anyString() );
doCallRealMethod().when( databaseMeta ).setDatabaseInterface( any( DatabaseInterface.class ) );
doReturn( "\"" ).when( databaseMeta ).getStartQuote();
doReturn( "\"" ).when( databaseMeta ).getEndQuote();
final DatabaseInterface databaseInterface = mock( DatabaseInterface.class );
doReturn( true ).when( databaseInterface ).isQuoteAllFields();
databaseMeta.setDatabaseInterface( databaseInterface );
final RowMeta fields = new RowMeta();
for ( int i = 0; i < 10; i++ ) {
final ValueMetaInterface valueMeta = new ValueMetaNone( "test_" + i );
fields.addValueMeta( valueMeta );
}
for ( int i = 0; i < 10; i++ ) {
databaseMeta.quoteReservedWords( fields );
}
for ( int i = 0; i < 10; i++ ) {
databaseMeta.quoteReservedWords( fields );
final String name = fields.getValueMeta( i ).getName();
// check valueMeta index in list
assertTrue( name.contains( "test_" + i ) );
// check valueMeta is found by quoted name
assertNotNull( fields.searchValueMeta( name ) );
}
}
@Test
public void testModifyingName() throws Exception {
DatabaseMeta databaseMeta = mock( DatabaseMeta.class );
OracleDatabaseMeta odbm = new OracleDatabaseMeta();
doCallRealMethod().when( databaseMeta ).setDatabaseInterface( any( DatabaseInterface.class ) );
doCallRealMethod().when( databaseMeta ).setName( anyString() );
doCallRealMethod().when( databaseMeta ).getName();
doCallRealMethod().when( databaseMeta ).getDisplayName();
databaseMeta.setDatabaseInterface( odbm );
databaseMeta.setName( "test" );
List<DatabaseMeta> list = new ArrayList<DatabaseMeta>();
list.add( databaseMeta );
DatabaseMeta databaseMeta2 = mock( DatabaseMeta.class );
OracleDatabaseMeta odbm2 = new OracleDatabaseMeta();
doCallRealMethod().when( databaseMeta2 ).setDatabaseInterface( any( DatabaseInterface.class ) );
doCallRealMethod().when( databaseMeta2 ).setName( anyString() );
doCallRealMethod().when( databaseMeta2 ).getName();
doCallRealMethod().when( databaseMeta2 ).setDisplayName( anyString() );
doCallRealMethod().when( databaseMeta2 ).getDisplayName();
doCallRealMethod().when( databaseMeta2 ).verifyAndModifyDatabaseName( any( ArrayList.class ), anyString() );
databaseMeta2.setDatabaseInterface( odbm2 );
databaseMeta2.setName( "test" );
databaseMeta2.verifyAndModifyDatabaseName( list, null );
assertTrue( !databaseMeta.getDisplayName().equals( databaseMeta2.getDisplayName() ) );
}
@Test
public void testGetFeatureSummary() throws Exception {
DatabaseMeta databaseMeta = mock( DatabaseMeta.class );
OracleDatabaseMeta odbm = new OracleDatabaseMeta();
doCallRealMethod().when( databaseMeta ).setDatabaseInterface( any( DatabaseInterface.class ) );
doCallRealMethod().when( databaseMeta ).getFeatureSummary();
doCallRealMethod().when( databaseMeta ).getAttributes();
databaseMeta.setDatabaseInterface( odbm );
List<RowMetaAndData> result = databaseMeta.getFeatureSummary();
assertNotNull( result );
for ( RowMetaAndData rmd : result ) {
assertEquals( 2, rmd.getRowMeta().size() );
assertEquals( "Parameter", rmd.getRowMeta().getValueMeta( 0 ).getName() );
assertEquals( ValueMetaInterface.TYPE_STRING, rmd.getRowMeta().getValueMeta( 0 ).getType() );
assertEquals( "Value", rmd.getRowMeta().getValueMeta( 1 ).getName() );
assertEquals( ValueMetaInterface.TYPE_STRING, rmd.getRowMeta().getValueMeta( 1 ).getType() );
}
}
@Test
public void indexOfName_NullArray() {
assertEquals( -1, DatabaseMeta.indexOfName( null, "" ) );
}
@Test
public void indexOfName_NullName() {
assertEquals( -1, DatabaseMeta.indexOfName( new String[] { "1" }, null ) );
}
@Test
public void indexOfName_ExactMatch() {
assertEquals( 1, DatabaseMeta.indexOfName( new String[] { "a", "b", "c" }, "b" ) );
}
@Test
public void indexOfName_NonExactMatch() {
assertEquals( 1, DatabaseMeta.indexOfName( new String[] { "a", "b", "c" }, "B" ) );
}
/**
* Given that the {@link DatabaseInterface} object is of a new extended type.
* <br/>
* When {@link DatabaseMeta#getDropTableIfExistsStatement(String)} is called,
* then the underlying new method of {@link DatabaseInterfaceExtended} should be used.
*/
@Test
public void shouldCallNewMethodWhenDatabaseInterfaceIsOfANewType() {
DatabaseInterfaceExtended databaseInterfaceNew = mock( DatabaseInterfaceExtended.class );
databaseMeta.setDatabaseInterface( databaseInterfaceNew );
when( databaseInterfaceNew.getDropTableIfExistsStatement( TABLE_NAME ) ).thenReturn( DROP_STATEMENT );
String statement = databaseMeta.getDropTableIfExistsStatement( TABLE_NAME );
assertEquals( DROP_STATEMENT, statement );
}
/**
* Given that the {@link DatabaseInterface} object is of an old type.
* <br/>
* When {@link DatabaseMeta#getDropTableIfExistsStatement(String)} is called,
* then a fallback statement should be returned.
*/
@Test
public void shouldFallBackWhenDatabaseInterfaceIsOfAnOldType() {
String statement = databaseMeta.getDropTableIfExistsStatement( TABLE_NAME );
assertEquals( DROP_STATEMENT_FALLBACK, statement );
}
@Test
public void databases_WithSameDbConnTypes_AreTheSame() {
DatabaseInterface mssqlServerDatabaseMeta = new MSSQLServerDatabaseMeta();
mssqlServerDatabaseMeta.setPluginId( "MSSQL" );
assertTrue( databaseMeta.databaseForBothDbInterfacesIsTheSame( mssqlServerDatabaseMeta, mssqlServerDatabaseMeta ) );
}
@Test
public void databases_WithSameDbConnTypes_AreNotSame_IfPluginIdIsNull() {
DatabaseInterface mssqlServerDatabaseMeta = new MSSQLServerDatabaseMeta();
mssqlServerDatabaseMeta.setPluginId( null );
assertFalse( databaseMeta.databaseForBothDbInterfacesIsTheSame( mssqlServerDatabaseMeta, mssqlServerDatabaseMeta ) );
}
@Test
public void databases_WithDifferentDbConnTypes_AreDifferent_IfNonOfThemIsSubsetOfAnother() {
DatabaseInterface mssqlServerDatabaseMeta = new MSSQLServerDatabaseMeta();
mssqlServerDatabaseMeta.setPluginId( "MSSQL" );
DatabaseInterface oracleDatabaseMeta = new OracleDatabaseMeta();
oracleDatabaseMeta.setPluginId( "ORACLE" );
assertFalse( databaseMeta.databaseForBothDbInterfacesIsTheSame( mssqlServerDatabaseMeta, oracleDatabaseMeta ) );
}
@Test
public void databases_WithDifferentDbConnTypes_AreTheSame_IfOneConnTypeIsSubsetOfAnother_2LevelHierarchy() {
DatabaseInterface mssqlServerDatabaseMeta = new MSSQLServerDatabaseMeta();
mssqlServerDatabaseMeta.setPluginId( "MSSQL" );
DatabaseInterface mssqlServerNativeDatabaseMeta = new MSSQLServerNativeDatabaseMeta();
mssqlServerNativeDatabaseMeta.setPluginId( "MSSQLNATIVE" );
assertTrue( databaseMeta.databaseForBothDbInterfacesIsTheSame( mssqlServerDatabaseMeta,
mssqlServerNativeDatabaseMeta ) );
}
@Test
public void databases_WithDifferentDbConnTypes_AreTheSame_IfOneConnTypeIsSubsetOfAnother_3LevelHierarchy() {
class MSSQLServerNativeDatabaseMetaChild extends MSSQLServerDatabaseMeta {
@Override
public String getPluginId() {
return "MSSQLNATIVE_CHILD";
}
}
DatabaseInterface mssqlServerDatabaseMeta = new MSSQLServerDatabaseMeta();
mssqlServerDatabaseMeta.setPluginId( "MSSQL" );
DatabaseInterface mssqlServerNativeDatabaseMetaChild = new MSSQLServerNativeDatabaseMetaChild();
assertTrue(
databaseMeta.databaseForBothDbInterfacesIsTheSame( mssqlServerDatabaseMeta, mssqlServerNativeDatabaseMetaChild ) );
}
@Test
public void testCheckParameters() {
DatabaseMeta meta = mock( DatabaseMeta.class );
BaseDatabaseMeta databaseInterface = mock( BaseDatabaseMeta.class );
when( databaseInterface.requiresName() ).thenReturn( true );
when( meta.getDatabaseInterface() ).thenReturn( databaseInterface );
when( meta.getName() ).thenReturn( null );
when( meta.isPartitioned() ).thenReturn( false );
when( meta.checkParameters() ).thenCallRealMethod();
assertEquals( 2, meta.checkParameters().length );
}
@Test
public void setSQLServerInstanceTest() {
DatabaseMeta dbmeta = new DatabaseMeta();
DatabaseInterface mssqlServerDatabaseMeta = new MSSQLServerDatabaseMeta();
mssqlServerDatabaseMeta.setPluginId( "MSSQL" );
DatabaseInterface mssqlServerNativeDatabaseMeta = new MSSQLServerNativeDatabaseMeta();
mssqlServerNativeDatabaseMeta.setPluginId( "MSSQLNATIVE" );
dbmeta.setDatabaseInterface( mssqlServerDatabaseMeta );
dbmeta.setSQLServerInstance( "" );
assertEquals( dbmeta.getSQLServerInstance(), null );
dbmeta.setSQLServerInstance( "instance1" );
assertEquals( dbmeta.getSQLServerInstance(), "instance1" );
dbmeta.setDatabaseInterface( mssqlServerNativeDatabaseMeta );
dbmeta.setSQLServerInstance( "" );
assertEquals( dbmeta.getSQLServerInstance(), null );
dbmeta.setSQLServerInstance( "instance1" );
assertEquals( dbmeta.getSQLServerInstance(), "instance1" );
}
@Test
public void testAddOptionsMysql() {
DatabaseMeta databaseMeta = new DatabaseMeta( "", "Mysql", "JDBC", null, "stub:stub", null, null, null );
Map<String, String> options = databaseMeta.getExtraOptions();
if ( !options.keySet().contains( "MYSQL.defaultFetchSize" ) ) {
fail();
}
}
@Test
public void testAddOptionsMariaDB() {
DatabaseMeta databaseMeta = new DatabaseMeta( "", "MariaDB", "JDBC", null, "stub:stub", null, null, null );
Map<String, String> options = databaseMeta.getExtraOptions();
if ( !options.keySet().contains( "MARIADB.defaultFetchSize" ) ) {
fail();
}
}
@Test
public void testAddOptionsInfobright() {
DatabaseMeta databaseMeta = new DatabaseMeta( "", "Infobright", "JDBC", null, "stub:stub", null, null, null );
Map<String, String> options = databaseMeta.getExtraOptions();
if ( !options.keySet().contains( "INFOBRIGHT.characterEncoding" ) ) {
fail();
}
}
}