/*! ******************************************************************************
*
* 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.repository.kdr.delegates;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;
import org.pentaho.di.core.Const;
import org.pentaho.di.core.RowMetaAndData;
import org.pentaho.di.core.database.Database;
import org.pentaho.di.core.database.DatabaseMeta;
import org.pentaho.di.core.encryption.Encr;
import org.pentaho.di.core.exception.KettleDatabaseException;
import org.pentaho.di.core.exception.KettleDependencyException;
import org.pentaho.di.core.exception.KettleException;
import org.pentaho.di.core.exception.KettleValueException;
import org.pentaho.di.core.row.RowMeta;
import org.pentaho.di.core.row.RowMetaInterface;
import org.pentaho.di.core.row.value.ValueMetaInteger;
import org.pentaho.di.core.row.value.ValueMetaString;
import org.pentaho.di.i18n.BaseMessages;
import org.pentaho.di.repository.LongObjectId;
import org.pentaho.di.repository.ObjectId;
import org.pentaho.di.repository.RepositoryOperation;
import org.pentaho.di.repository.kdr.KettleDatabaseRepository;
public class KettleDatabaseRepositoryDatabaseDelegate extends KettleDatabaseRepositoryBaseDelegate {
private static final Class<?> PKG = DatabaseMeta.class; // for i18n purposes, needed by Translator2!!
public KettleDatabaseRepositoryDatabaseDelegate( KettleDatabaseRepository repository ) {
super( repository );
}
public synchronized ObjectId getDatabaseID( String name ) throws KettleException {
return repository.connectionDelegate.getIDWithValue(
quoteTable( KettleDatabaseRepository.TABLE_R_DATABASE ),
quote( KettleDatabaseRepository.FIELD_DATABASE_ID_DATABASE ),
quote( KettleDatabaseRepository.FIELD_DATABASE_NAME ), name );
}
public synchronized String getDatabaseTypeCode( ObjectId id_database_type ) throws KettleException {
return repository.connectionDelegate.getStringWithID(
quoteTable( KettleDatabaseRepository.TABLE_R_DATABASE_TYPE ),
quote( KettleDatabaseRepository.FIELD_DATABASE_TYPE_ID_DATABASE_TYPE ), id_database_type,
quote( KettleDatabaseRepository.FIELD_DATABASE_TYPE_CODE ) );
}
public synchronized String getDatabaseConTypeCode( ObjectId id_database_contype ) throws KettleException {
return repository.connectionDelegate.getStringWithID(
quoteTable( KettleDatabaseRepository.TABLE_R_DATABASE_CONTYPE ),
quote( KettleDatabaseRepository.FIELD_DATABASE_CONTYPE_ID_DATABASE_CONTYPE ), id_database_contype,
quote( KettleDatabaseRepository.FIELD_DATABASE_CONTYPE_CODE ) );
}
public RowMetaAndData getDatabase( ObjectId id_database ) throws KettleException {
return repository.connectionDelegate.getOneRow(
quoteTable( KettleDatabaseRepository.TABLE_R_DATABASE ),
quote( KettleDatabaseRepository.FIELD_DATABASE_ID_DATABASE ), id_database );
}
public RowMetaAndData getDatabaseAttribute( ObjectId id_database_attribute ) throws KettleException {
return repository.connectionDelegate.getOneRow(
quoteTable( KettleDatabaseRepository.TABLE_R_DATABASE_ATTRIBUTE ),
quote( KettleDatabaseRepository.FIELD_DATABASE_ATTRIBUTE_ID_DATABASE_ATTRIBUTE ), id_database_attribute );
}
public Collection<RowMetaAndData> getDatabaseAttributes() throws KettleDatabaseException, KettleValueException {
List<RowMetaAndData> attrs = new ArrayList<RowMetaAndData>();
List<Object[]> rows =
repository.connectionDelegate.getRows( "SELECT * FROM "
+ quoteTable( KettleDatabaseRepository.TABLE_R_DATABASE_ATTRIBUTE ), 0 );
for ( Object[] row : rows ) {
RowMetaAndData rowWithMeta = new RowMetaAndData( repository.connectionDelegate.getReturnRowMeta(), row );
long id =
rowWithMeta.getInteger(
quote( KettleDatabaseRepository.FIELD_DATABASE_ATTRIBUTE_ID_DATABASE_ATTRIBUTE ), 0 );
if ( id > 0 ) {
attrs.add( rowWithMeta );
}
}
return attrs;
}
/**
*
* Load the Database Info
*/
public DatabaseMeta loadDatabaseMeta( ObjectId id_database ) throws KettleException {
DatabaseMeta databaseMeta = new DatabaseMeta();
try {
RowMetaAndData r = getDatabase( id_database );
if ( r != null ) {
ObjectId id_database_type =
new LongObjectId( r.getInteger( KettleDatabaseRepository.FIELD_DATABASE_ID_DATABASE_TYPE, 0 ) ); // con_type
String dbTypeDesc = getDatabaseTypeCode( id_database_type );
if ( dbTypeDesc != null ) {
databaseMeta.setDatabaseInterface( DatabaseMeta.getDatabaseInterface( dbTypeDesc ) );
databaseMeta.setAttributes( new Properties() ); // new attributes
}
databaseMeta.setObjectId( id_database );
databaseMeta.setName( r.getString( KettleDatabaseRepository.FIELD_DATABASE_NAME, "" ) );
ObjectId id_database_contype = new LongObjectId(
r.getInteger( KettleDatabaseRepository.FIELD_DATABASE_ID_DATABASE_CONTYPE, 0 ) ); // con_access
databaseMeta.setAccessType( DatabaseMeta.getAccessType( getDatabaseConTypeCode( id_database_contype ) ) );
databaseMeta.setHostname( r.getString( KettleDatabaseRepository.FIELD_DATABASE_HOST_NAME, "" ) );
databaseMeta.setDBName( r.getString( KettleDatabaseRepository.FIELD_DATABASE_DATABASE_NAME, "" ) );
databaseMeta.setDBPort( r.getString( KettleDatabaseRepository.FIELD_DATABASE_PORT, "" ) );
databaseMeta.setUsername( r.getString( KettleDatabaseRepository.FIELD_DATABASE_USERNAME, "" ) );
databaseMeta.setPassword( Encr.decryptPasswordOptionallyEncrypted( r.getString(
KettleDatabaseRepository.FIELD_DATABASE_PASSWORD, "" ) ) );
databaseMeta.setServername( r.getString( KettleDatabaseRepository.FIELD_DATABASE_SERVERNAME, "" ) );
databaseMeta.setDataTablespace( r.getString( KettleDatabaseRepository.FIELD_DATABASE_DATA_TBS, "" ) );
databaseMeta.setIndexTablespace( r.getString( KettleDatabaseRepository.FIELD_DATABASE_INDEX_TBS, "" ) );
// Also, load all the properties we can find...
final Collection<RowMetaAndData> attrs = repository.connectionDelegate.getDatabaseAttributes( id_database );
for ( RowMetaAndData row : attrs ) {
String code = row.getString( KettleDatabaseRepository.FIELD_DATABASE_ATTRIBUTE_CODE, "" );
String attribute = row.getString( KettleDatabaseRepository.FIELD_DATABASE_ATTRIBUTE_VALUE_STR, "" );
databaseMeta.getAttributes().put( code, Const.NVL( attribute, "" ) );
}
}
return databaseMeta;
} catch ( KettleDatabaseException dbe ) {
throw new KettleException( "Error loading database connection from repository (id_database="
+ id_database + ")", dbe );
}
}
/**
* Saves the database information into a given repository.
*
* @param databaseMeta
* The database metadata object to store
*
* @throws KettleException
* if an error occurs.
*/
public void saveDatabaseMeta( DatabaseMeta databaseMeta ) throws KettleException {
try {
// If we don't have an ID, we don't know which entry in the database we need to update.
// See if a database with the same name is already available...
if ( databaseMeta.getObjectId() == null ) {
databaseMeta.setObjectId( getDatabaseID( databaseMeta.getName() ) );
}
// Still not found? --> Insert
if ( databaseMeta.getObjectId() == null ) {
// Insert new Note in repository
//
databaseMeta.setObjectId( insertDatabase(
databaseMeta.getName(), databaseMeta.getPluginId(), DatabaseMeta.getAccessTypeDesc( databaseMeta
.getAccessType() ), databaseMeta.getHostname(), databaseMeta.getDatabaseName(), databaseMeta
.getDatabasePortNumberString(), databaseMeta.getUsername(), databaseMeta.getPassword(),
databaseMeta.getServername(), databaseMeta.getDataTablespace(), databaseMeta.getIndexTablespace() ) );
} else {
// --> found entry with the same name...
// Update the note...
updateDatabase(
databaseMeta.getObjectId(), databaseMeta.getName(), databaseMeta.getPluginId(), DatabaseMeta
.getAccessTypeDesc( databaseMeta.getAccessType() ), databaseMeta.getHostname(), databaseMeta
.getDatabaseName(), databaseMeta.getDatabasePortNumberString(), databaseMeta.getUsername(),
databaseMeta.getPassword(), databaseMeta.getServername(), databaseMeta.getDataTablespace(),
databaseMeta.getIndexTablespace() );
}
// For the extra attributes, just delete them and re-add them.
delDatabaseAttributes( databaseMeta.getObjectId() );
// OK, now get a list of all the attributes set on the database connection...
insertDatabaseAttributes( databaseMeta.getObjectId(), databaseMeta.getAttributes() );
} catch ( KettleDatabaseException dbe ) {
throw new KettleException(
"Error saving database connection or one of its attributes to the repository.", dbe );
}
}
public synchronized ObjectId insertDatabase( String name, String type, String access, String host,
String dbname, String port, String user, String pass, String servername, String data_tablespace,
String index_tablespace ) throws KettleException {
ObjectId id = repository.connectionDelegate.getNextDatabaseID();
ObjectId id_database_type = getDatabaseTypeID( type );
if ( id_database_type == null ) {
// New support database type: add it!
id_database_type = repository.connectionDelegate.getNextDatabaseTypeID();
String tablename = KettleDatabaseRepository.TABLE_R_DATABASE_TYPE;
RowMetaInterface tableMeta = new RowMeta();
tableMeta.addValueMeta( new ValueMetaInteger(
KettleDatabaseRepository.FIELD_DATABASE_TYPE_ID_DATABASE_TYPE, 5, 0 ) );
tableMeta.addValueMeta( new ValueMetaString( KettleDatabaseRepository.FIELD_DATABASE_TYPE_CODE,
KettleDatabaseRepository.REP_STRING_CODE_LENGTH, 0 ) );
tableMeta.addValueMeta( new ValueMetaString( KettleDatabaseRepository.FIELD_DATABASE_TYPE_DESCRIPTION,
KettleDatabaseRepository.REP_STRING_LENGTH, 0 ) );
repository.connectionDelegate.getDatabase().prepareInsert( tableMeta, tablename );
Object[] tableData = new Object[3];
int tableIndex = 0;
tableData[tableIndex++] = new LongObjectId( id_database_type ).longValue();
tableData[tableIndex++] = type;
tableData[tableIndex++] = type;
repository.connectionDelegate.getDatabase().setValuesInsert( tableMeta, tableData );
repository.connectionDelegate.getDatabase().insertRow();
repository.connectionDelegate.getDatabase().closeInsert();
}
ObjectId id_database_contype = getDatabaseConTypeID( access );
RowMetaAndData table = new RowMetaAndData();
table.addValue( new ValueMetaInteger(
KettleDatabaseRepository.FIELD_DATABASE_ID_DATABASE ), id );
table.addValue(
new ValueMetaString( KettleDatabaseRepository.FIELD_DATABASE_NAME ), name );
table
.addValue(
new ValueMetaInteger(
KettleDatabaseRepository.FIELD_DATABASE_ID_DATABASE_TYPE ),
id_database_type );
table.addValue(
new ValueMetaInteger(
KettleDatabaseRepository.FIELD_DATABASE_ID_DATABASE_CONTYPE ),
id_database_contype );
table.addValue( new ValueMetaString(
KettleDatabaseRepository.FIELD_DATABASE_HOST_NAME ), host );
table.addValue( new ValueMetaString(
KettleDatabaseRepository.FIELD_DATABASE_DATABASE_NAME ), dbname );
table.addValue(
new ValueMetaInteger( KettleDatabaseRepository.FIELD_DATABASE_PORT ), Long.valueOf(
Const.toLong( port, -1 ) ) );
table.addValue( new ValueMetaString(
KettleDatabaseRepository.FIELD_DATABASE_USERNAME ), user );
table.addValue( new ValueMetaString(
KettleDatabaseRepository.FIELD_DATABASE_PASSWORD ), Encr
.encryptPasswordIfNotUsingVariables( pass ) );
table.addValue( new ValueMetaString(
KettleDatabaseRepository.FIELD_DATABASE_SERVERNAME ), servername );
table.addValue( new ValueMetaString(
KettleDatabaseRepository.FIELD_DATABASE_DATA_TBS ), data_tablespace );
table.addValue( new ValueMetaString(
KettleDatabaseRepository.FIELD_DATABASE_INDEX_TBS ), index_tablespace );
repository.connectionDelegate.getDatabase().prepareInsert(
table.getRowMeta(), KettleDatabaseRepository.TABLE_R_DATABASE );
repository.connectionDelegate.getDatabase().setValuesInsert( table );
repository.connectionDelegate.getDatabase().insertRow();
repository.connectionDelegate.getDatabase().closeInsert();
return id;
}
public synchronized void updateDatabase( ObjectId id_database, String name, String type, String access,
String host, String dbname, String port, String user, String pass, String servername,
String data_tablespace, String index_tablespace ) throws KettleException {
ObjectId id_database_type = getDatabaseTypeID( type );
ObjectId id_database_contype = getDatabaseConTypeID( access );
RowMetaAndData table = new RowMetaAndData();
table.addValue(
new ValueMetaString( KettleDatabaseRepository.FIELD_DATABASE_NAME ), name );
table
.addValue(
new ValueMetaInteger(
KettleDatabaseRepository.FIELD_DATABASE_ID_DATABASE_TYPE ),
id_database_type );
table.addValue(
new ValueMetaInteger(
KettleDatabaseRepository.FIELD_DATABASE_ID_DATABASE_CONTYPE ),
id_database_contype );
table.addValue( new ValueMetaString(
KettleDatabaseRepository.FIELD_DATABASE_HOST_NAME ), host );
table.addValue( new ValueMetaString(
KettleDatabaseRepository.FIELD_DATABASE_DATABASE_NAME ), dbname );
table.addValue(
new ValueMetaInteger( KettleDatabaseRepository.FIELD_DATABASE_PORT ), Long.valueOf(
Const.toLong( port, -1 ) ) );
table.addValue( new ValueMetaString(
KettleDatabaseRepository.FIELD_DATABASE_USERNAME ), user );
table.addValue( new ValueMetaString(
KettleDatabaseRepository.FIELD_DATABASE_PASSWORD ), Encr
.encryptPasswordIfNotUsingVariables( pass ) );
table.addValue( new ValueMetaString(
KettleDatabaseRepository.FIELD_DATABASE_SERVERNAME ), servername );
table.addValue( new ValueMetaString(
KettleDatabaseRepository.FIELD_DATABASE_DATA_TBS ), data_tablespace );
table.addValue( new ValueMetaString(
KettleDatabaseRepository.FIELD_DATABASE_INDEX_TBS ), index_tablespace );
repository.connectionDelegate.updateTableRow(
KettleDatabaseRepository.TABLE_R_DATABASE, KettleDatabaseRepository.FIELD_DATABASE_ID_DATABASE, table,
id_database );
}
public synchronized ObjectId getDatabaseTypeID( String code ) throws KettleException {
return repository.connectionDelegate.getIDWithValue(
quoteTable( KettleDatabaseRepository.TABLE_R_DATABASE_TYPE ),
quote( KettleDatabaseRepository.FIELD_DATABASE_TYPE_ID_DATABASE_TYPE ),
quote( KettleDatabaseRepository.FIELD_DATABASE_TYPE_CODE ), code );
}
public synchronized ObjectId getDatabaseConTypeID( String code ) throws KettleException {
return repository.connectionDelegate.getIDWithValue(
quoteTable( KettleDatabaseRepository.TABLE_R_DATABASE_CONTYPE ),
quote( KettleDatabaseRepository.FIELD_DATABASE_CONTYPE_ID_DATABASE_CONTYPE ),
quote( KettleDatabaseRepository.FIELD_DATABASE_CONTYPE_CODE ), code );
}
/**
* Remove a database connection from the repository
*
* @param databaseName
* The name of the connection to remove
* @throws KettleException
* In case something went wrong: database error, insufficient permissions, depending objects, etc.
*/
public void deleteDatabaseMeta( String databaseName ) throws KettleException {
repository.getSecurityProvider().validateAction( RepositoryOperation.DELETE_DATABASE );
try {
ObjectId id_database = getDatabaseID( databaseName );
delDatabase( id_database );
} catch ( KettleException dbe ) {
throw new KettleException( BaseMessages.getString(
PKG, "KettleDatabaseRepository.Exception.ErrorDeletingConnection.Message", databaseName ), dbe );
}
}
public synchronized void delDatabase( ObjectId id_database ) throws KettleException {
repository.getSecurityProvider().validateAction( RepositoryOperation.DELETE_DATABASE );
// First, see if the database connection is still used by other connections...
// If so, generate an error!!
// We look in table R_STEP_DATABASE to see if there are any steps using this database.
//
String[] transList = repository.getTransformationsUsingDatabase( id_database );
String[] jobList = repository.getJobsUsingDatabase( id_database );
if ( jobList.length == 0 && transList.length == 0 ) {
repository.connectionDelegate.performDelete( "DELETE FROM "
+ quoteTable( KettleDatabaseRepository.TABLE_R_DATABASE ) + " WHERE "
+ quote( KettleDatabaseRepository.FIELD_DATABASE_ID_DATABASE ) + " = ? ", id_database );
} else {
String message = " Database used by the following " + Const.CR;
if ( jobList.length > 0 ) {
message = "jobs :" + Const.CR;
for ( String job : jobList ) {
message += "\t " + job + Const.CR;
}
}
message += "transformations:" + Const.CR;
for ( String trans : transList ) {
message += "\t " + trans + Const.CR;
}
KettleDependencyException e = new KettleDependencyException( message );
throw new KettleDependencyException( "This database is still in use by "
+ jobList.length + " jobs and " + transList.length + " transformations references", e );
}
}
public synchronized void delDatabaseAttributes( ObjectId id_database ) throws KettleException {
repository.connectionDelegate.performDelete( "DELETE FROM "
+ quoteTable( KettleDatabaseRepository.TABLE_R_DATABASE_ATTRIBUTE ) + " WHERE "
+ quote( KettleDatabaseRepository.FIELD_DATABASE_ATTRIBUTE_ID_DATABASE ) + " = ? ", id_database );
}
public synchronized int getNrDatabases() throws KettleException {
int retval = 0;
String sql = "SELECT COUNT(*) FROM " + quoteTable( KettleDatabaseRepository.TABLE_R_DATABASE );
RowMetaAndData r = repository.connectionDelegate.getOneRow( sql );
if ( r != null ) {
retval = (int) r.getInteger( 0, 0L );
}
return retval;
}
public synchronized int getNrDatabases( ObjectId id_transformation ) throws KettleException {
int retval = 0;
RowMetaAndData transIdRow = repository.connectionDelegate.getParameterMetaData( id_transformation );
String sql =
"SELECT COUNT(*) FROM "
+ quoteTable( KettleDatabaseRepository.TABLE_R_STEP_DATABASE ) + " WHERE "
+ quote( KettleDatabaseRepository.FIELD_STEP_DATABASE_ID_TRANSFORMATION ) + " = ? ";
RowMetaAndData r =
repository.connectionDelegate.getOneRow( sql, transIdRow.getRowMeta(), transIdRow.getData() );
if ( r != null ) {
retval = (int) r.getInteger( 0, 0L );
}
return retval;
}
public synchronized int getNrDatabaseAttributes( ObjectId id_database ) throws KettleException {
int retval = 0;
String sql =
"SELECT COUNT(*) FROM "
+ quoteTable( KettleDatabaseRepository.TABLE_R_DATABASE_ATTRIBUTE ) + " WHERE "
+ quote( KettleDatabaseRepository.FIELD_DATABASE_ATTRIBUTE_ID_DATABASE ) + " = " + id_database;
RowMetaAndData r = repository.connectionDelegate.getOneRow( sql );
if ( r != null ) {
retval = (int) r.getInteger( 0, 0L );
}
return retval;
}
private RowMetaAndData createAttributeRow( ObjectId idDatabase, String code, String strValue )
throws KettleException {
ObjectId id = repository.connectionDelegate.getNextDatabaseAttributeID();
RowMetaAndData table = new RowMetaAndData();
table.addValue(
new ValueMetaInteger(
KettleDatabaseRepository.FIELD_DATABASE_ATTRIBUTE_ID_DATABASE_ATTRIBUTE ), id );
table.addValue(
new ValueMetaInteger(
KettleDatabaseRepository.FIELD_DATABASE_ATTRIBUTE_ID_DATABASE ),
idDatabase );
table.addValue( new ValueMetaString(
KettleDatabaseRepository.FIELD_DATABASE_ATTRIBUTE_CODE ), code );
table.addValue( new ValueMetaString(
KettleDatabaseRepository.FIELD_DATABASE_ATTRIBUTE_VALUE_STR ), strValue );
return table;
}
private void insertDatabaseAttributes( ObjectId idDatabase, Properties properties ) throws KettleException {
if ( properties.isEmpty() ) {
return;
}
Database db = repository.connectionDelegate.getDatabase();
boolean firstAttribute = true;
Enumeration<Object> keys = properties.keys();
while ( keys.hasMoreElements() ) {
String code = (String) keys.nextElement();
String attribute = (String) properties.get( code );
RowMetaAndData attributeData = createAttributeRow( idDatabase, code, attribute );
if ( firstAttribute ) {
db.prepareInsert( attributeData.getRowMeta(), KettleDatabaseRepository.TABLE_R_DATABASE_ATTRIBUTE );
firstAttribute = false;
}
db.setValuesInsert( attributeData );
db.insertRow( db.getPrepStatementInsert(), true, false );
}
db.executeAndClearBatch( db.getPrepStatementInsert() );
db.closeInsert();
}
}