/*! ****************************************************************************** * * Pentaho Data Integration * * Copyright (C) 2002-2016 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.trans.steps.combinationlookup; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; import java.sql.ResultSet; import org.apache.commons.lang.StringUtils; import org.junit.Assert; import org.junit.Before; import org.pentaho.di.core.Const; import org.pentaho.di.core.KettleEnvironment; import org.pentaho.di.core.SQLStatement; import org.pentaho.di.core.database.Database; import org.pentaho.di.core.database.DatabaseMeta; import org.pentaho.di.core.exception.KettleException; import org.pentaho.di.core.plugins.PluginRegistry; import org.pentaho.di.core.plugins.StepPluginType; 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.ValueMetaInteger; import org.pentaho.di.core.row.value.ValueMetaString; import org.pentaho.di.repository.Repository; import org.pentaho.di.trans.Trans; import org.pentaho.di.trans.TransHopMeta; import org.pentaho.di.trans.TransMeta; import org.pentaho.di.trans.step.StepMeta; import org.pentaho.di.trans.steps.tableinput.TableInputMeta; import org.pentaho.metastore.api.IMetaStore; import junit.framework.TestCase; /** * Test class for combination lookup/update. HSQL is used as database in memory to get an easy playground for database * tests. H2 does not support all SQL features but it should proof enough for most of our tests. * * @author Sven Boden */ public class CombinationLookupTest extends TestCase { public static final String[] databasesXML = { "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + "<connection>" + "<name>lookup</name>" + "<server>127.0.0.1</server>" + "<type>H2</type>" + "<access>Native</access>" + "<database>mem:db</database>" + "<port></port>" + "<username>sa</username>" + "<password></password>" + "</connection>", }; private static String target_table = "type1_dim"; private static String source_table = "source"; private static String[] insertStatement = { // New rows for the target "INSERT INTO " + source_table + "(ORDNO, DLR_CD, DLR_NM, DLR_DESC)" + "VALUES (1, 'BE010001', 'Frijters', 'Frijters NV');", "INSERT INTO " + source_table + "(ORDNO, DLR_CD, DLR_NM, DLR_DESC)" + "VALUES (2, 'BE010002', 'Sebrechts', 'Sebrechts NV');", "INSERT INTO " + source_table + "(ORDNO, DLR_CD, DLR_NM, DLR_DESC)" + "VALUES (3, 'DE010003', 'Gelden', 'Gelden Distribution Center');", // Existing business key "INSERT INTO " + source_table + "(ORDNO, DLR_CD, DLR_NM, DLR_DESC)" + "VALUES (4, 'BE010001', 'Frijters', 'Frijters BVBA');", // New row again "INSERT INTO " + source_table + "(ORDNO, DLR_CD, DLR_NM, DLR_DESC)" + "VALUES (5, 'DE010004', 'Germania', 'German Distribution Center');" }; @Override @Before public void setUp() throws Exception { KettleEnvironment.init(); } public RowMetaInterface createTargetRowMetaInterface() { RowMetaInterface rm = new RowMeta(); ValueMetaInterface[] valuesMeta = { new ValueMetaInteger( "ID", 8, 0 ), new ValueMetaString( "DLR_CD", 8, 0 ), new ValueMetaString( "DLR_NM", 30, 0 ), new ValueMetaString( "DLR_DESC", 30, 0 ), }; for ( int i = 0; i < valuesMeta.length; i++ ) { rm.addValueMeta( valuesMeta[i] ); } return rm; } public RowMetaInterface createSourceRowMetaInterface() { RowMetaInterface rm = new RowMeta(); ValueMetaInterface[] valuesMeta = { new ValueMetaInteger( "ORDNO", 8, 0 ), new ValueMetaString( "DLR_CD", 8, 0 ), new ValueMetaString( "DLR_NM", 30, 0 ), new ValueMetaString( "DLR_DESC", 30, 0 ), }; for ( int i = 0; i < valuesMeta.length; i++ ) { rm.addValueMeta( valuesMeta[i] ); } return rm; } /** * Create source and target table. */ public void createTables( Database db ) throws Exception { String target = db.getCreateTableStatement( target_table, createTargetRowMetaInterface(), null, false, null, true ); try { db.execStatement( target ); } catch ( KettleException ex ) { fail( "failure while creating table " + target_table + ": " + ex.getMessage() ); } String source = db.getCreateTableStatement( source_table, createSourceRowMetaInterface(), null, false, null, true ); try { db.execStatement( source ); } catch ( KettleException ex ) { fail( "failure while creating table " + source_table + ": " + ex.getMessage() ); } } /** * Insert data in the source table. * * @param db * database to use. */ private void createData( Database db ) throws Exception { for ( int idx = 0; idx < insertStatement.length; idx++ ) { db.execStatement( insertStatement[idx] ); } } /** * Check the results in the target dimension table. * * @param db * database to use. */ public void checkResults( Database db ) throws Exception { String query = "SELECT ID, DLR_CD, DLR_NM, DLR_DESC FROM " + target_table + " ORDER BY ID"; String[] correctResults = { "1|BE010001|null|null", "2|BE010002|null|null", "3|DE010003|null|null", "4|DE010004|null|null", }; ResultSet rs = db.openQuery( query ); int idx = 0; while ( rs.next() ) { int id = rs.getInt( "ID" ); String dlr_cd = rs.getString( "DLR_CD" ); String dlr_nm = rs.getString( "DLR_NM" ); String dlr_desc = rs.getString( "DLR_DESC" ); String result = id + "|" + dlr_cd + "|" + dlr_nm + "|" + dlr_desc; if ( idx > correctResults.length ) { fail( "more rows returned than expected" ); } if ( !result.equals( correctResults[idx] ) ) { fail( "row " + ( idx + 1 ) + " is different than expected" ); } idx++; } if ( idx < correctResults.length ) { fail( "less rows returned than expected" ); } } public void testUseDefaultSchemaName() throws Exception { String schemaName = ""; String tableName = "tableName"; String schemaTable = "default.tableName"; String technicalKeyField = "technicalKeyField"; DatabaseMeta databaseMeta = spy( new DatabaseMeta( databasesXML[0] ) { @Override public String getFieldDefinition( ValueMetaInterface v, String tk, String pk, boolean use_autoinc ) { return "someValue"; } } ); when( databaseMeta.getQuotedSchemaTableCombination( schemaName, tableName ) ).thenReturn( schemaTable ); CombinationLookupMeta clm = new CombinationLookupMeta(); clm.setTechnicalKeyField( technicalKeyField ); clm.setKeyLookup( new String[] { "keyLookup1", "keyLookup2" } ); clm.setDatabaseMeta( databaseMeta ); clm.setTablename( tableName ); clm.setSchemaName( schemaName ); StepMeta stepMeta = mock( StepMeta.class ); RowMetaInterface rowMetaInterface = mock( RowMetaInterface.class ); when( rowMetaInterface.size() ).thenReturn( 1 ); Repository repository = mock( Repository.class ); IMetaStore metaStore = mock( IMetaStore.class ); SQLStatement sqlStatement = clm.getSQLStatements( new TransMeta(), stepMeta, rowMetaInterface, repository, metaStore ); String sql = sqlStatement.getSQL(); Assert.assertTrue( StringUtils.countMatches( sql, schemaTable ) == 3 ); } /** * Test case for Combination lookup/update. */ public void testCombinationLookup() throws Exception { // // Create a new transformation... // TransMeta transMeta = new TransMeta(); transMeta.setName( "transname" ); // Add the database connections for ( int i = 0; i < databasesXML.length; i++ ) { DatabaseMeta databaseMeta = new DatabaseMeta( databasesXML[i] ); transMeta.addDatabase( databaseMeta ); } DatabaseMeta lookupDBInfo = transMeta.findDatabase( "lookup" ); // Execute our setup SQLs in the database. Database lookupDatabase = new Database( transMeta, lookupDBInfo ); lookupDatabase.connect(); createTables( lookupDatabase ); createData( lookupDatabase ); PluginRegistry registry = PluginRegistry.getInstance(); // // create the source step... // String fromstepname = "read from [" + source_table + "]"; TableInputMeta tii = new TableInputMeta(); tii.setDatabaseMeta( transMeta.findDatabase( "lookup" ) ); String selectSQL = "SELECT " + Const.CR; selectSQL += "DLR_CD, DLR_NM, DLR_DESC "; selectSQL += "FROM " + source_table + " ORDER BY ORDNO;"; tii.setSQL( selectSQL ); String fromstepid = registry.getPluginId( StepPluginType.class, tii ); StepMeta fromstep = new StepMeta( fromstepid, fromstepname, tii ); fromstep.setLocation( 150, 100 ); fromstep.setDraw( true ); fromstep.setDescription( "Reads information from table [" + source_table + "] on database [" + lookupDBInfo + "]" ); transMeta.addStep( fromstep ); // // create the combination lookup/update step... // String lookupstepname = "lookup from [lookup]"; CombinationLookupMeta clm = new CombinationLookupMeta(); String[] lookupKey = { "DLR_CD" }; clm.setTablename( target_table ); clm.setKeyField( lookupKey ); clm.setKeyLookup( lookupKey ); clm.setTechnicalKeyField( "ID" ); clm.setTechKeyCreation( CombinationLookupMeta.CREATION_METHOD_TABLEMAX ); clm.setDatabaseMeta( lookupDBInfo ); String lookupstepid = registry.getPluginId( StepPluginType.class, clm ); StepMeta lookupstep = new StepMeta( lookupstepid, lookupstepname, clm ); lookupstep.setDescription( "Looks up information from table [lookup] on database [" + lookupDBInfo + "]" ); transMeta.addStep( lookupstep ); TransHopMeta hi = new TransHopMeta( fromstep, lookupstep ); transMeta.addTransHop( hi ); // Now execute the transformation... Trans trans = new Trans( transMeta ); trans.execute( null ); trans.waitUntilFinished(); checkResults( lookupDatabase ); } }