/*! ****************************************************************************** * * 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.sql.Timestamp; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.List; import org.pentaho.di.core.util.Utils; import org.pentaho.di.core.RowMetaAndData; import org.pentaho.di.core.exception.KettleDatabaseException; import org.pentaho.di.core.exception.KettleException; import org.pentaho.di.core.exception.KettleValueException; import org.pentaho.di.core.row.value.ValueMetaBase; import org.pentaho.di.core.row.value.ValueMetaInteger; import org.pentaho.di.core.row.value.ValueMetaString; import org.pentaho.di.core.row.value.timestamp.SimpleTimestampFormat; import org.pentaho.di.repository.LongObjectId; import org.pentaho.di.repository.ObjectId; import org.pentaho.di.repository.kdr.KettleDatabaseRepository; import org.pentaho.di.repository.kdr.delegates.metastore.KDBRMetaStoreAttribute; import org.pentaho.di.repository.kdr.delegates.metastore.KDBRMetaStoreElement; import org.pentaho.di.repository.kdr.delegates.metastore.KDBRMetaStoreElementType; import org.pentaho.metastore.api.IMetaStoreAttribute; import org.pentaho.metastore.api.IMetaStoreElement; import org.pentaho.metastore.api.IMetaStoreElementType; import org.pentaho.metastore.api.exceptions.MetaStoreException; public class KettleDatabaseRepositoryMetaStoreDelegate extends KettleDatabaseRepositoryBaseDelegate { public KettleDatabaseRepositoryMetaStoreDelegate( KettleDatabaseRepository repository ) { super( repository ); } /** * Retrieve the ID for a namespace * * @param namespace The namespace to look up * @return the ID of the namespace * @throws KettleException */ public synchronized LongObjectId getNamespaceId( String namespace ) throws KettleException { return repository.connectionDelegate.getIDWithValue( quoteTable( KettleDatabaseRepository.TABLE_R_NAMESPACE ), quote( KettleDatabaseRepository.FIELD_NAMESPACE_ID_NAMESPACE ), quote( KettleDatabaseRepository.FIELD_NAMESPACE_NAME ), namespace ); } public ObjectId verifyNamespace( String namespace ) throws MetaStoreException { try { ObjectId namespaceId = getNamespaceId( namespace ); if ( namespaceId == null ) { throw new MetaStoreException( "Unable to find namespace with name '" + namespace + "'" ); } return namespaceId; } catch ( KettleException e ) { throw new MetaStoreException( "Unable to get id of namespace '" + namespace + "'", e ); } } public synchronized LongObjectId getElementTypeId( LongObjectId namespaceId, String elementTypeName ) throws KettleException { return repository.connectionDelegate.getIDWithValue( quoteTable( KettleDatabaseRepository.TABLE_R_ELEMENT_TYPE ), quote( KettleDatabaseRepository.FIELD_ELEMENT_TYPE_ID_ELEMENT_TYPE ), quote( KettleDatabaseRepository.FIELD_ELEMENT_TYPE_NAME ), elementTypeName, new String[] { quote( KettleDatabaseRepository.FIELD_ELEMENT_TYPE_ID_NAMESPACE ), }, new ObjectId[] { namespaceId, } ); } public synchronized LongObjectId getElementId( LongObjectId elementTypeId, String elementName ) throws KettleException { return repository.connectionDelegate.getIDWithValue( quoteTable( KettleDatabaseRepository.TABLE_R_ELEMENT ), quote( KettleDatabaseRepository.FIELD_ELEMENT_ID_ELEMENT ), quote( KettleDatabaseRepository.FIELD_ELEMENT_NAME ), elementName, new String[] { quote( KettleDatabaseRepository.FIELD_ELEMENT_ID_ELEMENT_TYPE ), }, new ObjectId[] { elementTypeId, } ); } public RowMetaAndData getElementType( LongObjectId elementTypeId ) throws KettleException { return repository.connectionDelegate.getOneRow( quoteTable( KettleDatabaseRepository.TABLE_R_ELEMENT_TYPE ), quote( KettleDatabaseRepository.FIELD_ELEMENT_TYPE_ID_ELEMENT_TYPE ), elementTypeId ); } public RowMetaAndData getElement( LongObjectId elementId ) throws KettleException { return repository.connectionDelegate.getOneRow( quoteTable( KettleDatabaseRepository.TABLE_R_ELEMENT ), quote( KettleDatabaseRepository.FIELD_ELEMENT_ID_ELEMENT ), elementId ); } public RowMetaAndData getElementAttribute( LongObjectId elementAttributeId ) throws KettleException { return repository.connectionDelegate.getOneRow( quoteTable( KettleDatabaseRepository.TABLE_R_ELEMENT_ATTRIBUTE ), quote( KettleDatabaseRepository.FIELD_ELEMENT_ATTRIBUTE_ID_ELEMENT_ATTRIBUTE ), elementAttributeId ); } public Collection<RowMetaAndData> getNamespaces() throws KettleDatabaseException, KettleValueException { List<RowMetaAndData> attrs = new ArrayList<RowMetaAndData>(); String sql = "SELECT * FROM " + quoteTable( KettleDatabaseRepository.TABLE_R_NAMESPACE ); List<Object[]> rows = repository.connectionDelegate.getRows( sql, 0 ); for ( Object[] row : rows ) { RowMetaAndData rowWithMeta = new RowMetaAndData( repository.connectionDelegate.getReturnRowMeta(), row ); long id = rowWithMeta.getInteger( quote( KettleDatabaseRepository.FIELD_NAMESPACE_ID_NAMESPACE ), 0 ); if ( id > 0 ) { attrs.add( rowWithMeta ); } } return attrs; } public Collection<RowMetaAndData> getElementTypes( LongObjectId namespaceId ) throws KettleDatabaseException, KettleValueException { List<RowMetaAndData> attrs = new ArrayList<RowMetaAndData>(); String sql = "SELECT * FROM " + quoteTable( KettleDatabaseRepository.TABLE_R_ELEMENT_TYPE ) + " WHERE " + quote( KettleDatabaseRepository.FIELD_ELEMENT_TYPE_ID_NAMESPACE ) + " = " + namespaceId.getId(); List<Object[]> rows = repository.connectionDelegate.getRows( sql, 0 ); for ( Object[] row : rows ) { RowMetaAndData rowWithMeta = new RowMetaAndData( repository.connectionDelegate.getReturnRowMeta(), row ); long id = rowWithMeta.getInteger( quote( KettleDatabaseRepository.FIELD_ELEMENT_TYPE_ID_ELEMENT_TYPE ), 0 ); if ( id > 0 ) { attrs.add( rowWithMeta ); } } return attrs; } public Collection<RowMetaAndData> getElements( LongObjectId elementTypeId ) throws KettleDatabaseException, KettleValueException { List<RowMetaAndData> attrs = new ArrayList<RowMetaAndData>(); String sql = "SELECT * FROM " + quoteTable( KettleDatabaseRepository.TABLE_R_ELEMENT ) + " WHERE " + quote( KettleDatabaseRepository.FIELD_ELEMENT_ID_ELEMENT_TYPE ) + " = " + elementTypeId.getId(); List<Object[]> rows = repository.connectionDelegate.getRows( sql, 0 ); for ( Object[] row : rows ) { RowMetaAndData rowWithMeta = new RowMetaAndData( repository.connectionDelegate.getReturnRowMeta(), row ); long id = rowWithMeta.getInteger( quote( KettleDatabaseRepository.FIELD_ELEMENT_ID_ELEMENT_TYPE ), 0 ); if ( id > 0 ) { attrs.add( rowWithMeta ); } } return attrs; } public Collection<RowMetaAndData> getElementAttributes( ObjectId elementId, ObjectId parentAttributeId ) throws KettleDatabaseException, KettleValueException { List<RowMetaAndData> attrs = new ArrayList<RowMetaAndData>(); String sql = "SELECT * FROM " + quoteTable( KettleDatabaseRepository.TABLE_R_ELEMENT_ATTRIBUTE ) + " WHERE " + quote( KettleDatabaseRepository.FIELD_ELEMENT_ATTRIBUTE_ID_ELEMENT ) + " = " + elementId.getId(); if ( parentAttributeId != null ) { sql += " AND " + quote( KettleDatabaseRepository.FIELD_ELEMENT_ATTRIBUTE_ID_ELEMENT_ATTRIBUTE_PARENT ) + " = " + parentAttributeId.getId(); } List<Object[]> rows = repository.connectionDelegate.getRows( sql, 0 ); for ( Object[] row : rows ) { RowMetaAndData rowWithMeta = new RowMetaAndData( repository.connectionDelegate.getReturnRowMeta(), row ); long id = rowWithMeta.getInteger( quote( KettleDatabaseRepository.FIELD_ELEMENT_ATTRIBUTE_ID_ELEMENT_ATTRIBUTE ), 0 ); if ( id > 0 ) { attrs.add( rowWithMeta ); } } return attrs; } public ObjectId insertNamespace( String namespace ) throws KettleException { ObjectId idNamespace = repository.connectionDelegate.getNextID( quoteTable( KettleDatabaseRepository.TABLE_R_NAMESPACE ), quote( KettleDatabaseRepository.FIELD_NAMESPACE_ID_NAMESPACE ) ); RowMetaAndData table = new RowMetaAndData(); table.addValue( new ValueMetaInteger( KettleDatabaseRepository.FIELD_NAMESPACE_ID_NAMESPACE ), idNamespace ); table.addValue( new ValueMetaString( KettleDatabaseRepository.FIELD_NAMESPACE_NAME ), namespace ); repository.connectionDelegate.getDatabase().prepareInsert( table.getRowMeta(), KettleDatabaseRepository.TABLE_R_NAMESPACE ); repository.connectionDelegate.getDatabase().setValuesInsert( table ); repository.connectionDelegate.getDatabase().insertRow(); repository.connectionDelegate.getDatabase().closeInsert(); if ( log.isDebug() ) { log.logDebug( "Saved namespace [" + namespace + "]" ); } return idNamespace; } public ObjectId insertElementType( KDBRMetaStoreElementType type ) throws KettleException { ObjectId idType = repository.connectionDelegate.getNextID( quoteTable( KettleDatabaseRepository.TABLE_R_ELEMENT_TYPE ), quote( KettleDatabaseRepository.FIELD_ELEMENT_TYPE_ID_ELEMENT_TYPE ) ); RowMetaAndData table = new RowMetaAndData(); table.addValue( new ValueMetaInteger( KettleDatabaseRepository.FIELD_ELEMENT_TYPE_ID_ELEMENT_TYPE ), idType ); table.addValue( new ValueMetaInteger( KettleDatabaseRepository.FIELD_ELEMENT_TYPE_ID_NAMESPACE ), type .getNamespaceId() ); table.addValue( new ValueMetaString( KettleDatabaseRepository.FIELD_ELEMENT_TYPE_NAME ), type.getName() ); table.addValue( new ValueMetaString( KettleDatabaseRepository.FIELD_ELEMENT_TYPE_DESCRIPTION ), type .getDescription() ); repository.connectionDelegate.getDatabase().prepareInsert( table.getRowMeta(), KettleDatabaseRepository.TABLE_R_ELEMENT_TYPE ); repository.connectionDelegate.getDatabase().setValuesInsert( table ); repository.connectionDelegate.getDatabase().insertRow(); repository.connectionDelegate.getDatabase().closeInsert(); type.setId( new LongObjectId( idType ) ); if ( log.isDebug() ) { log.logDebug( "Saved element type [" + type.getName() + "]" ); } return idType; } public ObjectId updateElementType( ObjectId namespaceId, ObjectId elementTypeId, IMetaStoreElementType type ) throws KettleException { RowMetaAndData table = new RowMetaAndData(); table.addValue( new ValueMetaInteger( KettleDatabaseRepository.FIELD_ELEMENT_TYPE_ID_ELEMENT_TYPE ), elementTypeId ); table.addValue( new ValueMetaInteger( KettleDatabaseRepository.FIELD_ELEMENT_TYPE_ID_NAMESPACE ), namespaceId ); table.addValue( new ValueMetaString( KettleDatabaseRepository.FIELD_ELEMENT_TYPE_NAME ), type.getName() ); table.addValue( new ValueMetaString( KettleDatabaseRepository.FIELD_ELEMENT_TYPE_DESCRIPTION ), type .getDescription() ); repository.connectionDelegate.updateTableRow( KettleDatabaseRepository.TABLE_R_ELEMENT_TYPE, KettleDatabaseRepository.FIELD_ELEMENT_TYPE_ID_ELEMENT_TYPE, table ); if ( log.isDebug() ) { log.logDebug( "Updated element type [" + type.getName() + "]" ); } return elementTypeId; } public KDBRMetaStoreElementType parseElementType( String namespace, ObjectId namespaceId, RowMetaAndData elementTypeRow ) throws KettleValueException { Long id = elementTypeRow.getInteger( KettleDatabaseRepository.FIELD_ELEMENT_TYPE_ID_ELEMENT_TYPE ); String name = elementTypeRow.getString( KettleDatabaseRepository.FIELD_ELEMENT_TYPE_NAME, null ); String description = elementTypeRow.getString( KettleDatabaseRepository.FIELD_ELEMENT_TYPE_DESCRIPTION, null ); KDBRMetaStoreElementType type = new KDBRMetaStoreElementType( this, namespace, namespaceId, name, description ); type.setId( new LongObjectId( id ) ); return type; } public void deleteElementType( ObjectId elementTypeId ) throws KettleException { repository.connectionDelegate.performDelete( "DELETE FROM " + quoteTable( KettleDatabaseRepository.TABLE_R_ELEMENT_TYPE ) + " WHERE " + quote( KettleDatabaseRepository.FIELD_ELEMENT_TYPE_ID_ELEMENT_TYPE ) + " = ? ", elementTypeId ); } public void deleteNamespace( ObjectId namespaceId ) throws KettleException { repository.connectionDelegate.performDelete( "DELETE FROM " + quoteTable( KettleDatabaseRepository.TABLE_R_NAMESPACE ) + " WHERE " + quote( KettleDatabaseRepository.FIELD_NAMESPACE_ID_NAMESPACE ) + " = ? ", namespaceId ); } public KDBRMetaStoreElement parseElement( IMetaStoreElementType elementType, RowMetaAndData elementRow ) throws KettleException { Long elementId = elementRow.getInteger( KettleDatabaseRepository.FIELD_ELEMENT_ID_ELEMENT ); String name = elementRow.getString( KettleDatabaseRepository.FIELD_ELEMENT_NAME, null ); KDBRMetaStoreElement element = new KDBRMetaStoreElement( this, elementType, Long.toString( elementId ), null ); element.setName( name ); // Now load the attributes... // addAttributes( element, new LongObjectId( elementId ), new LongObjectId( 0 ) ); return element; } private void addAttributes( IMetaStoreAttribute parentAttribute, ObjectId elementId, LongObjectId parentAttributeId ) throws KettleException { Collection<RowMetaAndData> attributeRows = getElementAttributes( elementId, parentAttributeId ); for ( RowMetaAndData attributeRow : attributeRows ) { KDBRMetaStoreAttribute attribute = parseAttribute( elementId, attributeRow ); parentAttribute.addChild( attribute ); // See if this attribute has children... addAttributes( attribute, elementId, attribute.getObjectId() ); } } private KDBRMetaStoreAttribute parseAttribute( ObjectId elementId, RowMetaAndData attributeRow ) throws KettleException { try { Long attributeId = attributeRow.getInteger( KettleDatabaseRepository.FIELD_ELEMENT_ATTRIBUTE_ID_ELEMENT_ATTRIBUTE ); String key = attributeRow.getString( KettleDatabaseRepository.FIELD_ELEMENT_ATTRIBUTE_KEY, null ); String value = attributeRow.getString( KettleDatabaseRepository.FIELD_ELEMENT_ATTRIBUTE_VALUE, null ); Object object = parseAttributeValue( value ); KDBRMetaStoreAttribute attribute = new KDBRMetaStoreAttribute( this, key, object ); attribute.setObjectId( new LongObjectId( attributeId ) ); return attribute; } catch ( Exception e ) { throw new KettleException( "Unable to parse attribute from attribute row: " + attributeRow.toString(), e ); } } /** * supported types: * <p/> * <pre> * S : String (java.lang.String) * D : Date (java.util.Date) * N : Number (Double) * I : Integer (Long) * B : Boolean (Boolean) * * Examples * S:Pentaho Data Integration * D:2013/08/23 17:25:00 * N:1039348.9347 * I:12345678 * B:true * </pre> * * @param value * @return The native value * @throws Exception in case of conversion trouble */ public Object parseAttributeValue( String value ) throws Exception { if ( Utils.isEmpty( value ) || value.length() < 3 ) { return null; } String valueString = value.substring( 2 ); char type = value.charAt( 0 ); switch ( type ) { case 'S': return valueString; case 'T': return new SimpleTimestampFormat( ValueMetaBase.DEFAULT_TIMESTAMP_FORMAT_MASK ).parse( valueString ); case 'D': return new SimpleDateFormat( ValueMetaBase.DEFAULT_DATE_FORMAT_MASK ).parse( valueString ); case 'N': return Double.valueOf( valueString ); case 'I': return Long.valueOf( valueString ); case 'B': return "true".equalsIgnoreCase( valueString ) || "y".equalsIgnoreCase( valueString ); default: throw new KettleException( "Unknown data type : " + type ); } } public String encodeAttributeValue( Object object ) throws Exception { if ( object == null ) { return ""; } if ( object instanceof String ) { return "S:" + object.toString(); } if ( object instanceof Timestamp ) { return "T:" + new SimpleTimestampFormat( ValueMetaBase.DEFAULT_TIMESTAMP_FORMAT_MASK ).format( (Timestamp) object ); } if ( object instanceof Date ) { return "D:" + new SimpleDateFormat( ValueMetaBase.DEFAULT_DATE_FORMAT_MASK ).format( (Date) object ); } if ( object instanceof Double ) { return "N:" + Double.toString( (Double) object ); } if ( object instanceof Long ) { return "I:" + Long.toString( (Long) object ); } if ( object instanceof Boolean ) { return "B:" + ( ( (Boolean) object ) ? "true" : "false" ); } throw new KettleException( "Can't encode object of class : " + object.getClass().getName() ); } public ObjectId insertElement( IMetaStoreElementType elementType, IMetaStoreElement element ) throws MetaStoreException { try { LongObjectId elementId = repository.connectionDelegate.getNextID( quoteTable( KettleDatabaseRepository.TABLE_R_ELEMENT ), quote( KettleDatabaseRepository.FIELD_ELEMENT_ID_ELEMENT ) ); RowMetaAndData table = new RowMetaAndData(); table.addValue( new ValueMetaInteger( KettleDatabaseRepository.FIELD_ELEMENT_ID_ELEMENT ), elementId .longValue() ); table.addValue( new ValueMetaInteger( KettleDatabaseRepository.FIELD_ELEMENT_ID_ELEMENT_TYPE ), Long .valueOf( elementType.getId() ) ); table.addValue( new ValueMetaString( KettleDatabaseRepository.FIELD_ELEMENT_NAME ), element .getName() ); repository.connectionDelegate.getDatabase().prepareInsert( table.getRowMeta(), KettleDatabaseRepository.TABLE_R_ELEMENT ); repository.connectionDelegate.getDatabase().setValuesInsert( table ); repository.connectionDelegate.getDatabase().insertRow(); repository.connectionDelegate.getDatabase().closeInsert(); element.setId( elementId.toString() ); // Now save the attributes // insertAttributes( element.getChildren(), elementId, new LongObjectId( 0L ) ); if ( log.isDebug() ) { log.logDebug( "Saved element with name [" + element.getName() + "]" ); } return elementId; } catch ( Exception e ) { throw new MetaStoreException( "Unable to create new element with name '" + element.getName() + "'", e ); } } private void insertAttributes( List<IMetaStoreAttribute> children, LongObjectId elementId, LongObjectId parentAttributeId ) throws Exception { for ( IMetaStoreAttribute child : children ) { LongObjectId attributeId = repository.connectionDelegate.getNextID( quoteTable( KettleDatabaseRepository.TABLE_R_ELEMENT_ATTRIBUTE ), quote( KettleDatabaseRepository.FIELD_ELEMENT_ATTRIBUTE_ID_ELEMENT_ATTRIBUTE ) ); RowMetaAndData table = new RowMetaAndData(); table.addValue( new ValueMetaInteger( KettleDatabaseRepository.FIELD_ELEMENT_ATTRIBUTE_ID_ELEMENT_ATTRIBUTE ), attributeId.longValue() ); table.addValue( new ValueMetaInteger( KettleDatabaseRepository.FIELD_ELEMENT_ATTRIBUTE_ID_ELEMENT ), elementId.longValue() ); table.addValue( new ValueMetaInteger( KettleDatabaseRepository.FIELD_ELEMENT_ATTRIBUTE_ID_ELEMENT_ATTRIBUTE_PARENT ), parentAttributeId.longValue() ); table.addValue( new ValueMetaString( KettleDatabaseRepository.FIELD_ELEMENT_ATTRIBUTE_KEY ), child.getId() ); table.addValue( new ValueMetaString( KettleDatabaseRepository.FIELD_ELEMENT_ATTRIBUTE_VALUE ), encodeAttributeValue( child.getValue() ) ); repository.connectionDelegate.getDatabase().prepareInsert( table.getRowMeta(), KettleDatabaseRepository.TABLE_R_ELEMENT_ATTRIBUTE ); repository.connectionDelegate.getDatabase().setValuesInsert( table ); repository.connectionDelegate.getDatabase().insertRow(); repository.connectionDelegate.getDatabase().closeInsert(); child.setId( attributeId.toString() ); } } public void deleteElement( ObjectId elementId ) throws KettleException { repository.connectionDelegate.performDelete( "DELETE FROM " + quoteTable( KettleDatabaseRepository.TABLE_R_ELEMENT_ATTRIBUTE ) + " WHERE " + quote( KettleDatabaseRepository.FIELD_ELEMENT_ATTRIBUTE_ID_ELEMENT ) + " = ? ", elementId ); repository.connectionDelegate.performDelete( "DELETE FROM " + quoteTable( KettleDatabaseRepository.TABLE_R_ELEMENT ) + " WHERE " + quote( KettleDatabaseRepository.FIELD_ELEMENT_ID_ELEMENT ) + " = ? ", elementId ); } }