/*! ******************************************************************************
*
* 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 static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.sql.ResultSet;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.pentaho.di.core.KettleClientEnvironment;
import org.pentaho.di.core.exception.KettleDatabaseException;
import org.pentaho.di.core.row.RowMetaInterface;
import org.pentaho.di.core.row.value.ValueMetaBigNumber;
import org.pentaho.di.core.row.value.ValueMetaBinary;
import org.pentaho.di.core.row.value.ValueMetaBoolean;
import org.pentaho.di.core.row.value.ValueMetaDate;
import org.pentaho.di.core.row.value.ValueMetaInteger;
import org.pentaho.di.core.row.value.ValueMetaInternetAddress;
import org.pentaho.di.core.row.value.ValueMetaNumber;
import org.pentaho.di.core.row.value.ValueMetaString;
import org.pentaho.di.core.row.value.ValueMetaTimestamp;
import org.pentaho.di.core.variables.Variables;
public class OracleDatabaseMetaTest {
private OracleDatabaseMeta nativeMeta, odbcMeta, ociMeta;
@Before
public void setupOnce() throws Exception {
nativeMeta = new OracleDatabaseMeta();
odbcMeta = new OracleDatabaseMeta();
ociMeta = new OracleDatabaseMeta();
nativeMeta.setAccessType( DatabaseMeta.TYPE_ACCESS_NATIVE );
odbcMeta.setAccessType( DatabaseMeta.TYPE_ACCESS_ODBC );
ociMeta.setAccessType( DatabaseMeta.TYPE_ACCESS_OCI );
KettleClientEnvironment.init();
}
@Test
public void testOverriddenSettings() throws Exception {
// Tests the settings of the Oracle Database Meta
// according to the features of the DB as we know them
assertEquals( 1521, nativeMeta.getDefaultDatabasePort() );
assertEquals( -1, odbcMeta.getDefaultDatabasePort() );
assertFalse( nativeMeta.supportsAutoInc() );
assertFalse( nativeMeta.needsToLockAllTables() );
assertEquals( "oracle.jdbc.driver.OracleDriver", nativeMeta.getDriverClass() );
assertEquals( "sun.jdbc.odbc.JdbcOdbcDriver", odbcMeta.getDriverClass() );
assertEquals( "jdbc:odbc:FOO", odbcMeta.getURL( null, null, "FOO" ) );
assertEquals( "jdbc:oracle:thin:@FOO:1024:BAR", nativeMeta.getURL( "FOO", "1024", "BAR" ) );
assertEquals( "jdbc:oracle:thin:@FOO:11:BAR", nativeMeta.getURL( "FOO", "11", ":BAR" ) );
assertEquals( "jdbc:oracle:thin:@BAR:65534/FOO", nativeMeta.getURL( "BAR", "65534", "/FOO" ) );
assertEquals( "jdbc:oracle:thin:@FOO", nativeMeta.getURL( "", "", "FOO" ) );
assertEquals( "jdbc:oracle:thin:@FOO", nativeMeta.getURL( null, "-1", "FOO" ) );
assertEquals( "jdbc:oracle:thin:@FOO", nativeMeta.getURL( null, null, "FOO" ) );
assertEquals( "jdbc:oracle:thin:@FOO:1234:BAR", nativeMeta.getURL( "FOO", "1234", "BAR" ) );
assertEquals( "jdbc:oracle:thin:@", nativeMeta.getURL( "", "", "" ) ); // Pretty sure this is a bug...
assertEquals( "jdbc:oracle:oci:@BAR", ociMeta.getURL( null, null, "BAR" ) );
assertEquals(
"jdbc:oracle:oci:@(description=(address=(host=FOO)(protocol=tcp)(port=9876))(connect_data=(sid=BAR)))", ociMeta
.getURL( "FOO", "9876", "BAR" ) );
try {
ociMeta.getURL( null, null, null );
fail( "Expected KettleDatabaseException here" );
} catch ( KettleDatabaseException expected ) {
// Keep going ...
}
try {
ociMeta.getURL( "", "", "" );
fail( "Expected KettleDatabaseException here" );
} catch ( KettleDatabaseException expected ) {
// Keep going ...
}
assertFalse( nativeMeta.supportsOptionsInURL() );
assertTrue( nativeMeta.supportsSequences() );
assertTrue( nativeMeta.supportsSequenceNoMaxValueOption() );
assertTrue( nativeMeta.useSchemaNameForTableList() );
assertTrue( nativeMeta.supportsSynonyms() );
String[] reservedWords =
new String[] { "ACCESS", "ADD", "ALL", "ALTER", "AND", "ANY", "ARRAYLEN", "AS", "ASC", "AUDIT", "BETWEEN", "BY",
"CHAR", "CHECK", "CLUSTER", "COLUMN", "COMMENT", "COMPRESS", "CONNECT", "CREATE", "CURRENT", "DATE",
"DECIMAL", "DEFAULT", "DELETE", "DESC", "DISTINCT", "DROP", "ELSE", "EXCLUSIVE", "EXISTS", "FILE", "FLOAT",
"FOR", "FROM", "GRANT", "GROUP", "HAVING", "IDENTIFIED", "IMMEDIATE", "IN", "INCREMENT", "INDEX", "INITIAL",
"INSERT", "INTEGER", "INTERSECT", "INTO", "IS", "LEVEL", "LIKE", "LOCK", "LONG", "MAXEXTENTS", "MINUS",
"MODE", "MODIFY", "NOAUDIT", "NOCOMPRESS", "NOT", "NOTFOUND", "NOWAIT", "NULL", "NUMBER", "OF", "OFFLINE",
"ON", "ONLINE", "OPTION", "OR", "ORDER", "PCTFREE", "PRIOR", "PRIVILEGES", "PUBLIC", "RAW", "RENAME",
"RESOURCE", "REVOKE", "ROW", "ROWID", "ROWLABEL", "ROWNUM", "ROWS", "SELECT", "SESSION", "SET", "SHARE",
"SIZE", "SMALLINT", "SQLBUF", "START", "SUCCESSFUL", "SYNONYM", "SYSDATE", "TABLE", "THEN", "TO", "TRIGGER",
"UID", "UNION", "UNIQUE", "UPDATE", "USER", "VALIDATE", "VALUES", "VARCHAR", "VARCHAR2", "VIEW", "WHENEVER",
"WHERE", "WITH" };
assertArrayEquals( reservedWords, nativeMeta.getReservedWords() );
assertEquals( "http://download.oracle.com/docs/cd/B19306_01/java.102/b14355/urls.htm#i1006362", nativeMeta
.getExtraOptionsHelpText() );
assertArrayEquals( new String[] { "ojdbc14.jar", "orai18n.jar" }, nativeMeta.getUsedLibraries() );
assertTrue( nativeMeta.requiresCreateTablePrimaryKeyAppend() );
assertFalse( nativeMeta.supportsPreparedStatementMetadataRetrieval() );
String quoteTest1 = "FOO 'BAR' \r TEST \n";
String quoteTest2 = "FOO 'BAR' \\r TEST \\n";
assertEquals( "'FOO ''BAR'' '||chr(10)||' TEST '||chr(13)||''", nativeMeta.quoteSQLString( quoteTest1 ) );
assertEquals( "'FOO ''BAR'' \\r TEST \\n'", nativeMeta.quoteSQLString( quoteTest2 ) );
assertFalse( nativeMeta.releaseSavepoint() );
Variables v = new Variables();
v.setVariable( "FOOVARIABLE", "FOOVALUE" );
DatabaseMeta dm = new DatabaseMeta();
dm.setDatabaseInterface( nativeMeta );
assertEquals( "TABLESPACE FOOVALUE", nativeMeta.getTablespaceDDL( v, dm, "${FOOVARIABLE}" ) );
assertEquals( "", nativeMeta.getTablespaceDDL( v, dm, "" ) );
assertFalse( nativeMeta.supportsErrorHandlingOnBatchUpdates() );
assertTrue( nativeMeta.supportsRepository() );
assertEquals( 2000, nativeMeta.getMaxVARCHARLength() );
assertTrue( nativeMeta.supportsTimestampDataType() );
assertEquals( 32, nativeMeta.getMaxColumnsInIndex() );
}
@Test
public void testOverriddenSQLStatements() throws Exception {
assertEquals( " WHERE ROWNUM <= 5", nativeMeta.getLimitClause( 5 ) );
String reusedFieldsQuery = "SELECT * FROM FOO WHERE 1=0";;
assertEquals( reusedFieldsQuery, nativeMeta.getSQLQueryFields( "FOO" ) );
assertEquals( reusedFieldsQuery, nativeMeta.getSQLTableExists( "FOO" ) );
String reusedColumnsQuery = "SELECT FOO FROM BAR WHERE 1=0";
assertEquals( reusedColumnsQuery, nativeMeta.getSQLQueryColumnFields( "FOO", "BAR" ) );
assertEquals( reusedColumnsQuery, nativeMeta.getSQLColumnExists( "FOO", "BAR" ) );
assertEquals( "SELECT * FROM USER_SEQUENCES WHERE SEQUENCE_NAME = 'FOO'", nativeMeta.getSQLSequenceExists( "FOO" ) );
assertEquals( "SELECT * FROM USER_SEQUENCES WHERE SEQUENCE_NAME = 'FOO'", nativeMeta.getSQLSequenceExists( "foo" ) );
assertEquals( "SELECT * FROM ALL_SEQUENCES WHERE SEQUENCE_NAME = 'BAR' AND SEQUENCE_OWNER = 'FOO'", nativeMeta
.getSQLSequenceExists( "FOO.BAR" ) );
assertEquals( "SELECT * FROM ALL_SEQUENCES WHERE SEQUENCE_NAME = 'BAR' AND SEQUENCE_OWNER = 'FOO'", nativeMeta
.getSQLSequenceExists( "foo.bar" ) );
assertEquals( "SELECT FOO.currval FROM DUAL", nativeMeta.getSQLCurrentSequenceValue( "FOO" ) );
assertEquals( "SELECT FOO.nextval FROM dual", nativeMeta.getSQLNextSequenceValue( "FOO" ) );
assertEquals( "ALTER TABLE FOO ADD ( FOO TIMESTAMP ) ",
nativeMeta.getAddColumnStatement( "FOO", new ValueMetaTimestamp( "FOO" ), "", false, "", false ) );
assertEquals( "ALTER TABLE FOO ADD ( FOO DATE ) ", nativeMeta.getAddColumnStatement( "FOO", new ValueMetaDate( "FOO" ), "",
false, "", false ) );
assertEquals( "ALTER TABLE FOO ADD ( FOO VARCHAR2(15) ) ", nativeMeta.getAddColumnStatement( "FOO", new ValueMetaString(
"FOO", 15, 0 ), "", false, "", false ) );
assertEquals( "ALTER TABLE FOO ADD ( FOO INTEGER ) ", nativeMeta.getAddColumnStatement( "FOO", new ValueMetaInteger(
"FOO", 15, 0 ), "", false, "", false ) );
assertEquals( "ALTER TABLE FOO ADD ( FOO NUMBER(15, 10) ) ", nativeMeta.getAddColumnStatement( "FOO",
new ValueMetaBigNumber(
"FOO", 15, 10 ), "", false, "", false ) );
assertEquals( "ALTER TABLE FOO ADD ( FOO NUMBER(15, 10) ) ", nativeMeta.getAddColumnStatement( "FOO", new ValueMetaNumber(
"FOO", 15, 10 ), "", false, "", false ) );
assertEquals( "ALTER TABLE FOO ADD ( FOO BLOB ) ", nativeMeta.getAddColumnStatement( "FOO", new ValueMetaBinary(
"FOO", 2048, 0 ), "", false, "", false ) );
assertEquals( "ALTER TABLE FOO ADD ( FOO CHAR(1) ) ", nativeMeta.getAddColumnStatement( "FOO", new ValueMetaBoolean(
"FOO" ), "", false, "", false ) );
assertEquals( "ALTER TABLE FOO ADD ( FOO UNKNOWN ) ", nativeMeta.getAddColumnStatement( "FOO",
new ValueMetaInternetAddress( "FOO" ), "", false, "", false ) );
String lineSep = System.getProperty( "line.separator" );
assertEquals( "ALTER TABLE FOO DROP ( BAR ) " + lineSep, nativeMeta.getDropColumnStatement(
"FOO", new ValueMetaString( "BAR" ), "", false, "", false ) );
String modColStmtExpected =
"ALTER TABLE FOO ADD ( BAR_KTL VARCHAR2(2000) ) ;" + lineSep + "UPDATE FOO SET BAR_KTL=BAR;" + lineSep
+ "ALTER TABLE FOO DROP ( BAR ) " + lineSep + ";" + lineSep + "ALTER TABLE FOO ADD ( BAR VARCHAR2(2000) ) ;"
+ lineSep + "UPDATE FOO SET BAR=BAR_KTL;" + lineSep + "ALTER TABLE FOO DROP ( BAR_KTL ) " + lineSep;
assertEquals( modColStmtExpected, nativeMeta.getModifyColumnStatement( "FOO", new ValueMetaString( "BAR" ), "", false, "",
false ) );
modColStmtExpected =
"ALTER TABLE \"FOO\" ADD ( BAR_KTL VARCHAR2(2000) ) ;" + lineSep + "UPDATE \"FOO\" SET BAR_KTL=BAR;" + lineSep
+ "ALTER TABLE \"FOO\" DROP ( BAR ) " + lineSep + ";" + lineSep + "ALTER TABLE \"FOO\" ADD ( BAR VARCHAR2(2000) ) ;"
+ lineSep + "UPDATE \"FOO\" SET BAR=BAR_KTL;" + lineSep + "ALTER TABLE \"FOO\" DROP ( BAR_KTL ) " + lineSep;
assertEquals( modColStmtExpected, nativeMeta.getModifyColumnStatement( "\"FOO\"", new ValueMetaString( "BAR" ), "", false, "",
false ) );
modColStmtExpected =
"ALTER TABLE FOO ADD ( A12345678901234567890123456789_KTL VARCHAR2(2000) ) ;" + lineSep
+ "UPDATE FOO SET A12345678901234567890123456789_KTL=A1234567890123456789012345678901234567890;" + lineSep
+ "ALTER TABLE FOO DROP ( A1234567890123456789012345678901234567890 ) " + lineSep + ";" + lineSep
+ "ALTER TABLE FOO ADD ( A1234567890123456789012345678901234567890 VARCHAR2(2000) ) ;" + lineSep
+ "UPDATE FOO SET A1234567890123456789012345678901234567890=A12345678901234567890123456789_KTL;" + lineSep
+ "ALTER TABLE FOO DROP ( A12345678901234567890123456789_KTL ) " + lineSep;
assertEquals( modColStmtExpected, nativeMeta.getModifyColumnStatement( "FOO", new ValueMetaString( "A1234567890123456789012345678901234567890" ), "", false, "",
false ) );
String expectedProcSql =
"SELECT DISTINCT DECODE(package_name, NULL, '', package_name||'.') || object_name " + "FROM user_arguments "
+ "ORDER BY 1";
assertEquals( expectedProcSql, nativeMeta.getSQLListOfProcedures() );
String expectedLockOneItem = "LOCK TABLE FOO IN EXCLUSIVE MODE;" + lineSep;
assertEquals( expectedLockOneItem, nativeMeta.getSQLLockTables( new String[] { "FOO" } ) );
String expectedLockMultiItem =
"LOCK TABLE FOO IN EXCLUSIVE MODE;" + lineSep + "LOCK TABLE BAR IN EXCLUSIVE MODE;" + lineSep;
assertEquals( expectedLockMultiItem, nativeMeta.getSQLLockTables( new String[] { "FOO", "BAR" } ) );
assertNull( nativeMeta.getSQLUnlockTables( null ) ); // Commit unlocks tables
assertEquals( "SELECT SEQUENCE_NAME FROM all_sequences", nativeMeta.getSQLListOfSequences() );
assertEquals(
"BEGIN EXECUTE IMMEDIATE 'DROP TABLE FOO'; EXCEPTION WHEN OTHERS THEN IF SQLCODE != -942 THEN RAISE; END IF; END;",
nativeMeta.getDropTableIfExistsStatement( "FOO" ) );
}
@Test
public void testGetFieldDefinition() throws Exception {
assertEquals( "FOO DATE",
nativeMeta.getFieldDefinition( new ValueMetaDate( "FOO" ), "", "", false, true, false ) );
assertEquals( "TIMESTAMP",
nativeMeta.getFieldDefinition( new ValueMetaTimestamp( "FOO" ), "", "", false, false, false ) );
assertEquals( "CHAR(1)",
nativeMeta.getFieldDefinition( new ValueMetaBoolean( "FOO" ), "", "", false, false, false ) );
assertEquals( "NUMBER(5, 3)",
nativeMeta.getFieldDefinition( new ValueMetaNumber( "FOO", 5, 3 ), "", "", false, false, false ) );
assertEquals( "NUMBER(5)",
nativeMeta.getFieldDefinition( new ValueMetaBigNumber( "FOO", 5, 0 ), "", "", false, false, false ) );
assertEquals( "INTEGER",
nativeMeta.getFieldDefinition( new ValueMetaInteger( "FOO", 17, 0 ), "", "", false, false, false ) );
assertEquals( "CLOB",
nativeMeta.getFieldDefinition( new ValueMetaString( "FOO", DatabaseMeta.CLOB_LENGTH, 0 ), "", "", false, false, false ) );
assertEquals( "CHAR(1)",
nativeMeta.getFieldDefinition( new ValueMetaString( "FOO", 1, 0 ), "", "", false, false, false ) );
assertEquals( "VARCHAR2(15)",
nativeMeta.getFieldDefinition( new ValueMetaString( "FOO", 15, 0 ), "", "", false, false, false ) );
assertEquals( "VARCHAR2(2000)",
nativeMeta.getFieldDefinition( new ValueMetaString( "FOO" ), "", "", false, false, false ) );
assertEquals( "VARCHAR2(2000)",
nativeMeta.getFieldDefinition( new ValueMetaString( "FOO", nativeMeta.getMaxVARCHARLength(), 0 ), "", "", false, false, false ) );
assertEquals( "CLOB",
nativeMeta.getFieldDefinition( new ValueMetaString( "FOO", nativeMeta.getMaxVARCHARLength() + 1, 0 ), "", "", false, false, false ) );
assertEquals( "BLOB",
nativeMeta.getFieldDefinition( new ValueMetaBinary( "FOO", 45, 0 ), "", "", false, false, false ) );
assertEquals( " UNKNOWN",
nativeMeta.getFieldDefinition( new ValueMetaInternetAddress( "FOO" ), "", "", false, false, false ) );
assertEquals( " UNKNOWN" + System.getProperty( "line.separator" ),
nativeMeta.getFieldDefinition( new ValueMetaInternetAddress( "FOO" ), "", "", false, false, true ) );
}
private int rowCnt = 0;
private String[] row1 = new String[] { "ROW1COL1", "ROW1COL2" };
private String[] row2 = new String[] { "ROW2COL1", "ROW2COL2" };
@Test
public void testCheckIndexExists() throws Exception {
String expectedSQL = "SELECT * FROM USER_IND_COLUMNS WHERE TABLE_NAME = 'FOO'";
Database db = Mockito.mock( Database.class );
RowMetaInterface rm = Mockito.mock( RowMetaInterface.class );
ResultSet rs = Mockito.mock( ResultSet.class );
DatabaseMeta dm = Mockito.mock( DatabaseMeta.class );
Mockito.when( dm.getQuotedSchemaTableCombination( "", "FOO" ) ).thenReturn( "FOO" );
Mockito.when( rs.next() ).thenReturn( rowCnt < 2 );
Mockito.when( db.openQuery( expectedSQL ) ).thenReturn( rs );
Mockito.when( db.getReturnRowMeta() ).thenReturn( rm );
Mockito.when( rm.getString( row1, "COLUMN_NAME", "" ) ).thenReturn( "ROW1COL2" );
Mockito.when( rm.getString( row2, "COLUMN_NAME", "" ) ).thenReturn( "ROW2COL2" );
Mockito.when( db.getRow( rs ) ).thenAnswer( new Answer<Object[]>() {
@Override
public Object[] answer( InvocationOnMock invocation ) throws Throwable {
rowCnt++;
if ( rowCnt == 1 ) {
return row1;
} else if ( rowCnt == 2 ) {
return row2;
} else {
return null;
}
}
} );
Mockito.when( db.getDatabaseMeta() ).thenReturn( dm );
assertTrue( nativeMeta.checkIndexExists( db, "", "FOO", new String[] { "ROW1COL2", "ROW2COL2" } ) );
assertFalse( nativeMeta.checkIndexExists( db, "", "FOO", new String[] { "ROW2COL2", "NOTTHERE" } ) );
assertFalse( nativeMeta.checkIndexExists( db, "", "FOO", new String[] { "NOTTHERE", "ROW1COL2" } ) );
}
}