/*! ****************************************************************************** * * 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.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.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.ByteArrayInputStream; import java.math.BigDecimal; import java.sql.Blob; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import static org.junit.Assert.assertNull; import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; import org.pentaho.di.core.exception.KettleDatabaseException; 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; public class NeoviewDatabaseMetaTest { private NeoviewDatabaseMeta nativeMeta, odbcMeta; @Before public void setupBefore() { nativeMeta = new NeoviewDatabaseMeta(); nativeMeta.setAccessType( DatabaseMeta.TYPE_ACCESS_NATIVE ); odbcMeta = new NeoviewDatabaseMeta(); odbcMeta.setAccessType( DatabaseMeta.TYPE_ACCESS_ODBC ); } @Test public void testSettings() throws Exception { assertArrayEquals( new int[] { DatabaseMeta.TYPE_ACCESS_NATIVE, DatabaseMeta.TYPE_ACCESS_ODBC, DatabaseMeta.TYPE_ACCESS_JNDI }, nativeMeta.getAccessTypeList() ); assertEquals( 18650, nativeMeta.getDefaultDatabasePort() ); assertEquals( -1, odbcMeta.getDefaultDatabasePort() ); assertFalse( nativeMeta.supportsAutoInc() ); assertEquals( 0, nativeMeta.getNotFoundTK( true ) ); assertEquals( 0, nativeMeta.getNotFoundTK( false ) ); assertEquals( "com.hp.t4jdbc.HPT4Driver", nativeMeta.getDriverClass() ); assertEquals( "sun.jdbc.odbc.JdbcOdbcDriver", odbcMeta.getDriverClass() ); assertEquals( "jdbc:odbc:FOO", odbcMeta.getURL( "IGNORED", "IGNORED", "FOO" ) ); assertEquals( "jdbc:hpt4jdbc://FOO:BAR/:schema=WIBBLE", nativeMeta.getURL( "FOO", "BAR", "WIBBLE" ) ); assertEquals( "jdbc:hpt4jdbc://FOO:BAR/:schema=WIBBLE", nativeMeta.getURL( "FOO", "BAR", "schema=WIBBLE" ) ); assertEquals( "jdbc:hpt4jdbc://FOO:BAR/::catalog=abc:serverDataSource=foo:schema=wibble", nativeMeta.getURL( "FOO", "BAR", ":catalog=abc:serverDataSource=foo:schema=wibble" ) ); // also pretty sure this is broken (two colons before catalog assertEquals( "jdbc:hpt4jdbc://FOO:BAR/:catalog=abc:serverDataSource=foo:schema=wibble", nativeMeta.getURL( "FOO", "BAR", "catalog=abc:serverDataSource=foo:schema=wibble" ) ); assertEquals( "jdbc:hpt4jdbc://FOO:/:schema=WIBBLE", nativeMeta.getURL( "FOO", "", "WIBBLE" ) ); // Pretty sure this is a bug (colon after foo) assertTrue( nativeMeta.isFetchSizeSupported() ); assertTrue( nativeMeta.supportsOptionsInURL() ); assertTrue( nativeMeta.useSchemaNameForTableList() ); assertFalse( nativeMeta.supportsBitmapIndex() ); assertTrue( nativeMeta.supportsSynonyms() ); assertFalse( nativeMeta.needsToLockAllTables() ); assertTrue( nativeMeta.supportsGetBlob() ); assertEquals( "", nativeMeta.getLimitClause( 15 ) ); assertArrayEquals( new String[] { "hpt4jdbc.jar" }, nativeMeta.getUsedLibraries() ); assertArrayEquals( new String[] { "ACTION", "FOR", "PROTOTYPE", "ADD", "FOREIGN", "PUBLIC", "ADMIN", "FOUND", "READ", "AFTER", "FRACTION", "READS", "AGGREGATE", "FREE", "REAL", "ALIAS", "FROM", "RECURSIVE", "ALL", "FULL", "REF", "ALLOCATE", "FUNCTION", "REFERENCES", "ALTER", "GENERAL", "REFERENCING", "AND", "GET", "RELATIVE", "ANY", "GLOBAL", "REPLACE", "ARE", "GO", "RESIGNAL", "ARRAY", "GOTO", "RESTRICT", "AS", "GRANT", "RESULT", "ASC", "GROUP", "RETURN", "ASSERTION", "GROUPING", "RETURNS", "ASYNC", "HAVING", "REVOKE", "AT", "HOST", "RIGHT", "AUTHORIZATION", "HOUR", "ROLE", "AVG", "IDENTITY", "ROLLBACK", "BEFORE", "IF", "ROLLUP", "BEGIN", "IGNORE", "ROUTINE", "BETWEEN", "IMMEDIATE", "ROW", "BINARY", "IN", "ROWS", "BIT", "INDICATOR", "SAVEPOINT", "BIT_LENGTH", "INITIALLY", "SCHEMA", "BLOB", "INNER", "SCOPE", "BOOLEAN", "INOUT", "SCROLL", "BOTH", "INPUT", "SEARCH", "BREADTH", "INSENSITIVE", "SECOND", "BY", "INSERT", "SECTION", "CALL", "INT", "SELECT", "CASE", "INTEGER", "SENSITIVE", "CASCADE", "INTERSECT", "SESSION", "CASCADED", "INTERVAL", "SESSION_USER", "CAST", "INTO", "SET", "CATALOG", "IS", "SETS", "CHAR", "ISOLATION", "SIGNAL", "CHAR_LENGTH", "ITERATE", "SIMILAR", "CHARACTER", "JOIN", "SIZE", "CHARACTER_LENGTH", "KEY", "SMALLINT", "CHECK", "LANGUAGE", "SOME", "CLASS", "LARGE", "CLOB", "LAST", "SPECIFIC", "CLOSE", "LATERAL", "SPECIFICTYPE", "COALESCE", "LEADING", "SQL", "COLLATE", "LEAVE", "SQL_CHAR", "COLLATION", "LEFT", "SQL_DATE", "COLUMN", "LESS", "SQL_DECIMAL", "COMMIT", "LEVEL", "SQL_DOUBLE", "COMPLETION", "LIKE", "SQL_FLOAT", "CONNECT", "LIMIT", "SQL_INT", "CONNECTION", "LOCAL", "SQL_INTEGER", "CONSTRAINT", "LOCALTIME", "SQL_REAL", "CONSTRAINTS", "LOCALTIMESTAMP", "SQL_SMALLINT", "CONSTRUCTOR", "LOCATOR", "SQL_TIME", "CONTINUE", "LOOP", "SQL_TIMESTAMP", "CONVERT", "LOWER", "SQL_VARCHAR", "CORRESPONDING", "MAP", "SQLCODE", "COUNT", "MATCH", "SQLERROR", "CREATE", "MAX", "SQLEXCEPTION", "CROSS", "MIN", "SQLSTATE", "CUBE", "MINUTE", "SQLWARNING", "CURRENT", "MODIFIES", "STRUCTURE", "CURRENT_DATE", "MODIFY", "SUBSTRING", "CURRENT_PATH", "MODULE", "SUM", "CURRENT_ROLE", "MONTH", "SYSTEM_USER", "CURRENT_TIME", "NAMES", "TABLE", "CURRENT_TIMESTAMP", "NATIONAL", "TEMPORARY", "CURRENT_USER", "NATURAL", "TERMINATE", "CURSOR", "NCHAR", "TEST", "CYCLE", "NCLOB", "THAN", "DATE", "NEW", "THEN", "DATETIME", "NEXT", "THERE", "DAY", "NO", "TIME", "DEALLOCATE", "NONE", "TIMESTAMP", "DEC", "NOT", "TIMEZONE_HOUR", "DECIMAL", "NULL", "TIMEZONE_MINUTE", "DECLARE", "NULLIF", "TO", "DEFAULT", "NUMERIC", "TRAILING", "DEFERRABLE", "OBJECT", "TRANSACTION", "DEFERRED", "OCTET_LENGTH", "TRANSLATE", "DELETE", "OF", "TRANSLATION", "DEPTH", "OFF", "TRANSPOSE", "DEREF", "OID", "TREAT", "DESC", "OLD", "TRIGGER", "DESCRIBE", "ON", "TRIM", "DESCRIPTOR", "ONLY", "TRUE", "DESTROY", "OPEN", "UNDER", "DESTRUCTOR", "OPERATORS", "UNION", "DETERMINISTIC", "OPTION", "UNIQUE", "DIAGNOSTICS", "OR", "UNKNOWN", "DISTINCT", "ORDER", "UNNEST", "DICTIONARY", "ORDINALITY", "UPDATE", "DISCONNECT", "OTHERS", "UPPER", "DOMAIN", "OUT", "UPSHIFT", "DOUBLE", "OUTER", "USAGE", "DROP", "OUTPUT", "USER", "DYNAMIC", "OVERLAPS", "USING", "EACH", "PAD", "VALUE", "ELSE", "PARAMETER", "VALUES", "ELSEIF", "PARAMETERS", "VARCHAR", "END", "PARTIAL", "VARIABLE", "END-EXEC", "PENDANT", "VARYING", "EQUALS", "POSITION", "VIEW", "ESCAPE", "POSTFIX", "VIRTUAL", "EXCEPT", "PRECISION", "VISIBLE", "EXCEPTION", "PREFIX", "WAIT", "EXEC", "PREORDER", "WHEN", "EXECUTE", "PREPARE", "WHENEVER", "EXISTS", "PRESERVE", "WHERE", "EXTERNAL", "PRIMARY", "WHILE", "EXTRACT", "PRIOR", "WITH", "FALSE", "PRIVATE", "WITHOUT", "FETCH", "PRIVILEGES", "WORK", "FIRST", "PROCEDURE", "WRITE", "FLOAT", "PROTECTED", "YEAR", "ZONE" }, nativeMeta.getReservedWords() ); assertEquals( "http://docs.hp.com/en/busintellsol.html", nativeMeta.getExtraOptionsHelpText() ); assertEquals( 4028, nativeMeta.getMaxVARCHARLength() ); } @Test public void testSQLStatements() { String lineSep = System.getProperty( "line.separator" ); assertEquals( "SELECT [FIRST 1] * FROM FOO", nativeMeta.getSQLQueryFields( "FOO" ) ); assertEquals( "SELECT [FIRST 1] * FROM FOO", nativeMeta.getSQLTableExists( "FOO" ) ); assertEquals( "SELECT [FIRST 1] FOO FROM BAR", nativeMeta.getSQLQueryColumnFields( "FOO", "BAR" ) ); assertEquals( "SELECT [FIRST 1] FOO FROM BAR", nativeMeta.getSQLColumnExists( "FOO", "BAR" ) ); assertEquals( "DELETE FROM FOO", nativeMeta.getTruncateTableStatement( "FOO" ) ); assertEquals( "ALTER TABLE FOO ADD ( BAR VARCHAR(15) ) ", nativeMeta.getAddColumnStatement( "FOO", new ValueMetaString( "BAR", 15, 0 ), "", false, "", false ) ); assertEquals( "ALTER TABLE FOO DROP ( BAR ) " + lineSep, nativeMeta.getDropColumnStatement( "FOO", new ValueMetaString( "BAR", 15, 0 ), "", false, "", false ) ); assertEquals( "ALTER TABLE FOO MODIFY BAR VARCHAR(15)", nativeMeta.getModifyColumnStatement( "FOO", new ValueMetaString( "BAR", 15, 0 ), "", false, "", false ) ); // if all the others require parens, this is likely a bug assertEquals( "insert into FOO(FOOKEY, FOOVERSION) values (0, 1)", nativeMeta.getSQLInsertAutoIncUnknownDimensionRow( "FOO", "FOOKEY", "FOOVERSION" ) ); assertEquals( "LOCK TABLE FOO IN EXCLUSIVE MODE;" + lineSep + "LOCK TABLE BAR IN EXCLUSIVE MODE;" + lineSep, nativeMeta.getSQLLockTables( new String[] { "FOO", "BAR" } ) ); assertNull( nativeMeta.getSQLUnlockTables( new String[] { "FOO", "BAR" } ) ); } @Test public void testGetFieldDefinition() { assertEquals( "FOO TIMESTAMP", 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 ) ); // Primary/Tech Keys assertEquals( "INTEGER NOT NULL PRIMARY KEY", nativeMeta.getFieldDefinition( new ValueMetaBigNumber( "FOO", 8, 0 ), "", "FOO", true, false, false ) ); assertEquals( "INTEGER NOT NULL PRIMARY KEY", nativeMeta.getFieldDefinition( new ValueMetaNumber( "FOO", 10, 0 ), "FOO", "", false, false, false ) ); assertEquals( "INTEGER NOT NULL PRIMARY KEY", nativeMeta.getFieldDefinition( new ValueMetaBigNumber( "FOO", 8, 0 ), "", "FOO", false, false, false ) ); // Regular Integers assertEquals( "NUMERIC(10)", nativeMeta.getFieldDefinition( new ValueMetaNumber( "FOO", 10, 0 ), "", "", false, false, false ) ); assertEquals( "NUMERIC(18)", nativeMeta.getFieldDefinition( new ValueMetaBigNumber( "FOO", 18, 0 ), "", "", false, false, false ) ); assertEquals( "INTEGER", nativeMeta.getFieldDefinition( new ValueMetaInteger( "FOO", 9, 0 ), "", "", false, false, false ) ); assertEquals( "FLOAT", nativeMeta.getFieldDefinition( new ValueMetaBigNumber( "FOO", 19, 0 ), "", "", false, false, false ) ); assertEquals( "NUMERIC(10, 5)", nativeMeta.getFieldDefinition( new ValueMetaBigNumber( "FOO", 10, 5 ), "", "", false, false, false ) ); assertEquals( "FLOAT", nativeMeta.getFieldDefinition( new ValueMetaNumber( "FOO", 19, 5 ), "", "", false, false, false ) ); assertEquals( "NUMERIC(-7)", nativeMeta.getFieldDefinition( new ValueMetaBigNumber( "FOO", -7, -2 ), "", "", false, false, false ) ); // This is a bug... assertEquals( "NUMERIC(-7, 2)", nativeMeta.getFieldDefinition( new ValueMetaBigNumber( "FOO", -7, 2 ), "", "", false, false, false ) ); // This is a bug ... // String Types assertEquals( "VARCHAR(15)", nativeMeta.getFieldDefinition( new ValueMetaString( "FOO", 15, 0 ), "", "", false, false, false ) ); assertEquals( "VARCHAR(4028)", nativeMeta.getFieldDefinition( new ValueMetaString( "FOO", nativeMeta.getMaxVARCHARLength(), 0 ), "", "", false, false, false ) ); assertEquals( "CHAR(4029)", nativeMeta.getFieldDefinition( new ValueMetaString( "FOO", 4029, 0 ), "", "", false, false, false ) ); assertEquals( "CHAR(4036)", nativeMeta.getFieldDefinition( new ValueMetaString( "FOO", 4036, 0 ), "", "", false, false, false ) ); assertEquals( "CLOB", nativeMeta.getFieldDefinition( new ValueMetaString( "FOO", 4037, 0 ), "", "", false, false, false ) ); // Binary assertEquals( "BLOB", nativeMeta.getFieldDefinition( new ValueMetaBinary( "FOO", 4037, 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 ) ); } @Test public void testGetValueFromResultSet() throws Exception { Object rtn = null; ResultSet resultSet = Mockito.mock( ResultSet.class ); ResultSetMetaData metaData = Mockito.mock( ResultSetMetaData.class ); Mockito.when( resultSet.getMetaData() ).thenReturn( metaData ); Mockito.when( resultSet.getTimestamp( 1 ) ).thenReturn( new java.sql.Timestamp( 65535 ) ); Mockito.when( resultSet.getTime( 2 ) ).thenReturn( new java.sql.Time( 1000 ) ); Mockito.when( resultSet.getTimestamp( 3 ) ).thenReturn( new java.sql.Timestamp( 65535 ) ); // ValueMetaDate -> Timestamp ValueMetaTimestamp ts = new ValueMetaTimestamp( "FOO" ); ts.setOriginalColumnType( java.sql.Types.TIMESTAMP ); ValueMetaDate tm = new ValueMetaDate( "BAR" ); tm.setOriginalColumnType( java.sql.Types.TIME ); ValueMetaDate dt = new ValueMetaDate( "WIBBLE" ); dt.setOriginalColumnType( java.sql.Types.DATE ); rtn = nativeMeta.getValueFromResultSet( resultSet, ts, 0 ); assertNotNull( rtn ); assertEquals( "java.sql.Timestamp", rtn.getClass().getName() ); rtn = nativeMeta.getValueFromResultSet( resultSet, tm, 1 ); assertNotNull( rtn ); assertEquals( "java.sql.Time", rtn.getClass().getName() ); rtn = nativeMeta.getValueFromResultSet( resultSet, dt, 2 ); assertNotNull( rtn ); assertEquals( "java.sql.Timestamp", rtn.getClass().getName() ); Mockito.when( resultSet.wasNull() ).thenReturn( true ); rtn = nativeMeta.getValueFromResultSet( resultSet, new ValueMetaString( "WOBBLE" ), 3 ); assertNull( rtn ); // Verify that getDate is not called, getTime is called once, and getTimestamp was called 2 times (once for TimeStamp, once for Date) Mockito.verify( resultSet, Mockito.times( 0 ) ).getDate( Mockito.anyInt() ); Mockito.verify( resultSet, Mockito.times( 1 ) ).getTime( Mockito.anyInt() ); Mockito.verify( resultSet, Mockito.times( 2 ) ).getTimestamp( Mockito.anyInt() ); // Now that the date stuff is done, validate the behaviors of other aspects of getValueFromResultSet Mockito.when( resultSet.wasNull() ).thenReturn( false ); Mockito.when( resultSet.getBoolean( 1 ) ).thenReturn( new Boolean( true ) ); Mockito.when( resultSet.getDouble( 1 ) ).thenReturn( new Double( 15 ) ); Mockito.when( resultSet.getBigDecimal( 1 ) ).thenReturn( new BigDecimal( "15" ) ); Mockito.when( resultSet.getLong( 1 ) ).thenReturn( new Long( "15" ) ); Mockito.when( resultSet.getString( 1 ) ).thenReturn( "ASTRING" ); Mockito.when( resultSet.getBytes( 1 ) ).thenReturn( "ASTRING".getBytes() ); Blob mockBlob = Mockito.mock( Blob.class ); byte[] bytes = "FOO".getBytes(); ByteArrayInputStream bais = new ByteArrayInputStream( bytes ); Mockito.when( mockBlob.getBinaryStream() ).thenReturn( bais ); Mockito.when( mockBlob.length() ).thenReturn( new Long( bytes.length ) ); Mockito.when( mockBlob.getBytes( Mockito.anyLong(), Mockito.anyInt() ) ).thenReturn( bytes ); Mockito.when( resultSet.getBlob( 1 ) ).thenReturn( mockBlob ); rtn = nativeMeta.getValueFromResultSet( resultSet, new ValueMetaBoolean( "FOO" ), 0 ); assertNotNull( rtn ); assertTrue( rtn instanceof Boolean ); rtn = nativeMeta.getValueFromResultSet( resultSet, new ValueMetaNumber( "FOO", 15, 5 ), 0 ); assertNotNull( rtn ); assertTrue( rtn instanceof Double ); rtn = nativeMeta.getValueFromResultSet( resultSet, new ValueMetaBigNumber( "FOO", 15, 5 ), 0 ); assertNotNull( rtn ); assertTrue( rtn instanceof BigDecimal ); rtn = nativeMeta.getValueFromResultSet( resultSet, new ValueMetaInteger( "FOO", 5, 0 ), 0 ); assertNotNull( rtn ); assertTrue( rtn instanceof Long ); rtn = nativeMeta.getValueFromResultSet( resultSet, new ValueMetaString( "FOO", 25, 0 ), 0 ); assertNotNull( rtn ); assertTrue( rtn instanceof String ); ValueMetaString binStr = new ValueMetaString( "FOO" ); binStr.setStorageType( ValueMetaString.STORAGE_TYPE_BINARY_STRING ); rtn = nativeMeta.getValueFromResultSet( resultSet, binStr, 0 ); assertNotNull( rtn ); assertTrue( rtn instanceof byte[] ); rtn = nativeMeta.getValueFromResultSet( resultSet, new ValueMetaBinary( "FOO", 150, 0 ), 0 ); assertNotNull( rtn ); assertTrue( rtn instanceof byte[] ); try { Mockito.when( resultSet.getBoolean( 15 ) ).thenThrow( new SQLException( "Expected Exception Here" ) ); rtn = nativeMeta.getValueFromResultSet( resultSet, new ValueMetaBoolean( "FOO" ), 14 ); fail( "Should not get here" ); } catch ( Exception someException ) { assertTrue( someException instanceof KettleDatabaseException ); } } }