/*! ******************************************************************************
*
* 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.job.entries.mysqlbulkload;
import org.pentaho.di.job.entry.validator.AbstractFileValidator;
import org.pentaho.di.job.entry.validator.AndValidator;
import org.pentaho.di.job.entry.validator.JobEntryValidatorUtils;
import java.io.File;
import java.util.List;
import org.apache.commons.vfs2.FileObject;
import org.apache.commons.vfs2.provider.local.LocalFile;
import org.pentaho.di.cluster.SlaveServer;
import org.pentaho.di.core.CheckResultInterface;
import org.pentaho.di.core.Const;
import org.pentaho.di.core.util.Utils;
import org.pentaho.di.core.Result;
import org.pentaho.di.core.ResultFile;
import org.pentaho.di.core.database.Database;
import org.pentaho.di.core.database.DatabaseMeta;
import org.pentaho.di.core.exception.KettleDatabaseException;
import org.pentaho.di.core.exception.KettleException;
import org.pentaho.di.core.exception.KettleFileException;
import org.pentaho.di.core.exception.KettleXMLException;
import org.pentaho.di.core.variables.VariableSpace;
import org.pentaho.di.core.vfs.KettleVFS;
import org.pentaho.di.core.xml.XMLHandler;
import org.pentaho.di.i18n.BaseMessages;
import org.pentaho.di.job.JobMeta;
import org.pentaho.di.job.entry.JobEntryBase;
import org.pentaho.di.job.entry.JobEntryInterface;
import org.pentaho.di.job.entry.validator.ValidatorContext;
import org.pentaho.di.repository.ObjectId;
import org.pentaho.di.repository.Repository;
import org.pentaho.di.resource.ResourceEntry;
import org.pentaho.di.resource.ResourceEntry.ResourceType;
import org.pentaho.di.resource.ResourceReference;
import org.pentaho.metastore.api.IMetaStore;
import org.w3c.dom.Node;
/**
* This defines a MySQL job entry.
*
* @author Samatar Hassan
* @since Jan-2007
*/
public class JobEntryMysqlBulkLoad extends JobEntryBase implements Cloneable, JobEntryInterface {
private static Class<?> PKG = JobEntryMysqlBulkLoad.class; // for i18n purposes, needed by Translator2!!
private String schemaname;
private String tablename;
private String filename;
private String separator;
private String enclosed;
private String escaped;
private String linestarted;
private String lineterminated;
private String ignorelines;
private boolean replacedata;
private String listattribut;
private boolean localinfile;
public int prorityvalue;
private boolean addfiletoresult;
private DatabaseMeta connection;
public JobEntryMysqlBulkLoad( String n ) {
super( n, "" );
tablename = null;
schemaname = null;
filename = null;
separator = null;
enclosed = null;
escaped = null;
lineterminated = null;
linestarted = null;
replacedata = true;
ignorelines = "0";
listattribut = null;
localinfile = true;
connection = null;
addfiletoresult = false;
}
public JobEntryMysqlBulkLoad() {
this( "" );
}
public Object clone() {
JobEntryMysqlBulkLoad je = (JobEntryMysqlBulkLoad) super.clone();
return je;
}
public String getXML() {
StringBuilder retval = new StringBuilder( 200 );
retval.append( super.getXML() );
retval.append( " " ).append( XMLHandler.addTagValue( "schemaname", schemaname ) );
retval.append( " " ).append( XMLHandler.addTagValue( "tablename", tablename ) );
retval.append( " " ).append( XMLHandler.addTagValue( "filename", filename ) );
retval.append( " " ).append( XMLHandler.addTagValue( "separator", separator ) );
retval.append( " " ).append( XMLHandler.addTagValue( "enclosed", enclosed ) );
retval.append( " " ).append( XMLHandler.addTagValue( "escaped", escaped ) );
retval.append( " " ).append( XMLHandler.addTagValue( "linestarted", linestarted ) );
retval.append( " " ).append( XMLHandler.addTagValue( "lineterminated", lineterminated ) );
retval.append( " " ).append( XMLHandler.addTagValue( "replacedata", replacedata ) );
retval.append( " " ).append( XMLHandler.addTagValue( "ignorelines", ignorelines ) );
retval.append( " " ).append( XMLHandler.addTagValue( "listattribut", listattribut ) );
retval.append( " " ).append( XMLHandler.addTagValue( "localinfile", localinfile ) );
retval.append( " " ).append( XMLHandler.addTagValue( "prorityvalue", prorityvalue ) );
retval.append( " " ).append( XMLHandler.addTagValue( "addfiletoresult", addfiletoresult ) );
retval.append( " " ).append(
XMLHandler.addTagValue( "connection", connection == null ? null : connection.getName() ) );
return retval.toString();
}
public void loadXML( Node entrynode, List<DatabaseMeta> databases, List<SlaveServer> slaveServers,
Repository rep, IMetaStore metaStore ) throws KettleXMLException {
try {
super.loadXML( entrynode, databases, slaveServers );
schemaname = XMLHandler.getTagValue( entrynode, "schemaname" );
tablename = XMLHandler.getTagValue( entrynode, "tablename" );
filename = XMLHandler.getTagValue( entrynode, "filename" );
separator = XMLHandler.getTagValue( entrynode, "separator" );
enclosed = XMLHandler.getTagValue( entrynode, "enclosed" );
escaped = XMLHandler.getTagValue( entrynode, "escaped" );
linestarted = XMLHandler.getTagValue( entrynode, "linestarted" );
lineterminated = XMLHandler.getTagValue( entrynode, "lineterminated" );
replacedata = "Y".equalsIgnoreCase( XMLHandler.getTagValue( entrynode, "replacedata" ) );
ignorelines = XMLHandler.getTagValue( entrynode, "ignorelines" );
listattribut = XMLHandler.getTagValue( entrynode, "listattribut" );
localinfile = "Y".equalsIgnoreCase( XMLHandler.getTagValue( entrynode, "localinfile" ) );
prorityvalue = Const.toInt( XMLHandler.getTagValue( entrynode, "prorityvalue" ), -1 );
String dbname = XMLHandler.getTagValue( entrynode, "connection" );
addfiletoresult = "Y".equalsIgnoreCase( XMLHandler.getTagValue( entrynode, "addfiletoresult" ) );
connection = DatabaseMeta.findDatabase( databases, dbname );
} catch ( KettleException e ) {
throw new KettleXMLException( "Unable to load job entry of type 'Mysql bulk load' from XML node", e );
}
}
public void loadRep( Repository rep, IMetaStore metaStore, ObjectId id_jobentry, List<DatabaseMeta> databases,
List<SlaveServer> slaveServers ) throws KettleException {
try {
schemaname = rep.getJobEntryAttributeString( id_jobentry, "schemaname" );
tablename = rep.getJobEntryAttributeString( id_jobentry, "tablename" );
filename = rep.getJobEntryAttributeString( id_jobentry, "filename" );
separator = rep.getJobEntryAttributeString( id_jobentry, "separator" );
enclosed = rep.getJobEntryAttributeString( id_jobentry, "enclosed" );
escaped = rep.getJobEntryAttributeString( id_jobentry, "escaped" );
linestarted = rep.getJobEntryAttributeString( id_jobentry, "linestarted" );
lineterminated = rep.getJobEntryAttributeString( id_jobentry, "lineterminated" );
replacedata = rep.getJobEntryAttributeBoolean( id_jobentry, "replacedata" );
ignorelines = rep.getJobEntryAttributeString( id_jobentry, "ignorelines" );
listattribut = rep.getJobEntryAttributeString( id_jobentry, "listattribut" );
localinfile = rep.getJobEntryAttributeBoolean( id_jobentry, "localinfile" );
prorityvalue = (int) rep.getJobEntryAttributeInteger( id_jobentry, "prorityvalue" );
addfiletoresult = rep.getJobEntryAttributeBoolean( id_jobentry, "addfiletoresult" );
connection = rep.loadDatabaseMetaFromJobEntryAttribute( id_jobentry, "connection", "id_database", databases );
} catch ( KettleDatabaseException dbe ) {
throw new KettleException(
"Unable to load job entry of type 'Mysql bulk load' from the repository for id_jobentry=" + id_jobentry,
dbe );
}
}
public void saveRep( Repository rep, IMetaStore metaStore, ObjectId id_job ) throws KettleException {
try {
rep.saveJobEntryAttribute( id_job, getObjectId(), "schemaname", schemaname );
rep.saveJobEntryAttribute( id_job, getObjectId(), "tablename", tablename );
rep.saveJobEntryAttribute( id_job, getObjectId(), "filename", filename );
rep.saveJobEntryAttribute( id_job, getObjectId(), "separator", separator );
rep.saveJobEntryAttribute( id_job, getObjectId(), "enclosed", enclosed );
rep.saveJobEntryAttribute( id_job, getObjectId(), "escaped", escaped );
rep.saveJobEntryAttribute( id_job, getObjectId(), "linestarted", linestarted );
rep.saveJobEntryAttribute( id_job, getObjectId(), "lineterminated", lineterminated );
rep.saveJobEntryAttribute( id_job, getObjectId(), "replacedata", replacedata );
rep.saveJobEntryAttribute( id_job, getObjectId(), "ignorelines", ignorelines );
rep.saveJobEntryAttribute( id_job, getObjectId(), "listattribut", listattribut );
rep.saveJobEntryAttribute( id_job, getObjectId(), "localinfile", localinfile );
rep.saveJobEntryAttribute( id_job, getObjectId(), "prorityvalue", prorityvalue );
rep.saveJobEntryAttribute( id_job, getObjectId(), "addfiletoresult", addfiletoresult );
rep.saveDatabaseMetaJobEntryAttribute( id_job, getObjectId(), "connection", "id_database", connection );
} catch ( KettleDatabaseException dbe ) {
throw new KettleException(
"Unable to load job entry of type 'Mysql Bulk Load' to the repository for id_job=" + id_job, dbe );
}
}
public void setTablename( String tablename ) {
this.tablename = tablename;
}
public void setSchemaname( String schemaname ) {
this.schemaname = schemaname;
}
public String getSchemaname() {
return schemaname;
}
public String getTablename() {
return tablename;
}
public void setDatabase( DatabaseMeta database ) {
this.connection = database;
}
public DatabaseMeta getDatabase() {
return connection;
}
public boolean evaluates() {
return true;
}
public boolean isUnconditional() {
return true;
}
public Result execute( Result previousResult, int nr ) {
String ReplaceIgnore;
String IgnoreNbrLignes = "";
String ListOfColumn = "";
String LocalExec = "";
String PriorityText = "";
String LineTerminatedby = "";
String FieldTerminatedby = "";
Result result = previousResult;
result.setResult( false );
String vfsFilename = environmentSubstitute( filename );
// Let's check the filename ...
if ( !Utils.isEmpty( vfsFilename ) ) {
try {
// User has specified a file, We can continue ...
//
// This is running over VFS but we need a normal file.
// As such, we're going to verify that it's a local file...
// We're also going to convert VFS FileObject to File
//
FileObject fileObject = KettleVFS.getFileObject( vfsFilename, this );
if ( !( fileObject instanceof LocalFile ) ) {
// MySQL LOAD DATA can only use local files, so that's what we limit ourselves to.
//
throw new KettleException( "Only local files are supported at this time, file ["
+ vfsFilename + "] is not a local file." );
}
// Convert it to a regular platform specific file name
//
String realFilename = KettleVFS.getFilename( fileObject );
// Here we go... back to the regular scheduled program...
//
File file = new File( realFilename );
if ( ( file.exists() && file.canRead() ) || isLocalInfile() == false ) {
// User has specified an existing file, We can continue ...
if ( log.isDetailed() ) {
logDetailed( "File [" + realFilename + "] exists." );
}
if ( connection != null ) {
// User has specified a connection, We can continue ...
Database db = new Database( this, connection );
db.shareVariablesWith( this );
try {
db.connect( parentJob.getTransactionId(), null );
// Get schemaname
String realSchemaname = environmentSubstitute( schemaname );
// Get tablename
String realTablename = environmentSubstitute( tablename );
if ( db.checkTableExists( realTablename ) ) {
// The table existe, We can continue ...
if ( log.isDetailed() ) {
logDetailed( "Table [" + realTablename + "] exists." );
}
// Add schemaname (Most the time Schemaname.Tablename)
if ( schemaname != null ) {
realTablename = realSchemaname + "." + realTablename;
}
// Set the REPLACE or IGNORE
if ( isReplacedata() ) {
ReplaceIgnore = "REPLACE";
} else {
ReplaceIgnore = "IGNORE";
}
// Set the IGNORE LINES
if ( Const.toInt( getRealIgnorelines(), 0 ) > 0 ) {
IgnoreNbrLignes = "IGNORE " + getRealIgnorelines() + " LINES";
}
// Set list of Column
if ( getRealListattribut() != null ) {
ListOfColumn = "(" + MysqlString( getRealListattribut() ) + ")";
}
// Local File execution
if ( isLocalInfile() ) {
LocalExec = "LOCAL";
}
// Prority
if ( prorityvalue == 1 ) {
// LOW
PriorityText = "LOW_PRIORITY";
} else if ( prorityvalue == 2 ) {
// CONCURRENT
PriorityText = "CONCURRENT";
}
// Fields ....
if ( getRealSeparator() != null || getRealEnclosed() != null || getRealEscaped() != null ) {
FieldTerminatedby = "FIELDS ";
if ( getRealSeparator() != null ) {
FieldTerminatedby =
FieldTerminatedby
+ "TERMINATED BY '" + Const.replace( getRealSeparator(), "'", "''" ) + "'";
}
if ( getRealEnclosed() != null ) {
FieldTerminatedby =
FieldTerminatedby + " ENCLOSED BY '" + Const.replace( getRealEnclosed(), "'", "''" ) + "'";
}
if ( getRealEscaped() != null ) {
FieldTerminatedby =
FieldTerminatedby + " ESCAPED BY '" + Const.replace( getRealEscaped(), "'", "''" ) + "'";
}
}
// LINES ...
if ( getRealLinestarted() != null || getRealLineterminated() != null ) {
LineTerminatedby = "LINES ";
// Line starting By
if ( getRealLinestarted() != null ) {
LineTerminatedby =
LineTerminatedby
+ "STARTING BY '" + Const.replace( getRealLinestarted(), "'", "''" ) + "'";
}
// Line terminating By
if ( getRealLineterminated() != null ) {
LineTerminatedby =
LineTerminatedby
+ " TERMINATED BY '" + Const.replace( getRealLineterminated(), "'", "''" ) + "'";
}
}
String SQLBULKLOAD =
"LOAD DATA "
+ PriorityText + " " + LocalExec + " INFILE '" + realFilename.replace( '\\', '/' ) + "' "
+ ReplaceIgnore + " INTO TABLE " + realTablename + " " + FieldTerminatedby + " "
+ LineTerminatedby + " " + IgnoreNbrLignes + " " + ListOfColumn + ";";
try {
// Run the SQL
db.execStatement( SQLBULKLOAD );
// Everything is OK...we can deconnect now
db.disconnect();
if ( isAddFileToResult() ) {
// Add zip filename to output files
ResultFile resultFile =
new ResultFile(
ResultFile.FILE_TYPE_GENERAL, KettleVFS.getFileObject( realFilename, this ), parentJob
.getJobname(), toString() );
result.getResultFiles().put( resultFile.getFile().toString(), resultFile );
}
result.setResult( true );
} catch ( KettleDatabaseException je ) {
db.disconnect();
result.setNrErrors( 1 );
logError( "An error occurred executing this job entry : " + je.getMessage() );
} catch ( KettleFileException e ) {
logError( "An error occurred executing this job entry : " + e.getMessage() );
result.setNrErrors( 1 );
}
} else {
// Of course, the table should have been created already before the bulk load operation
db.disconnect();
result.setNrErrors( 1 );
if ( log.isDetailed() ) {
logDetailed( "Table [" + realTablename + "] doesn't exist!" );
}
}
} catch ( KettleDatabaseException dbe ) {
db.disconnect();
result.setNrErrors( 1 );
logError( "An error occurred executing this entry: " + dbe.getMessage() );
}
} else {
// No database connection is defined
result.setNrErrors( 1 );
logError( BaseMessages.getString( PKG, "JobMysqlBulkLoad.Nodatabase.Label" ) );
}
} else {
// the file doesn't exist
result.setNrErrors( 1 );
logError( "File [" + realFilename + "] doesn't exist!" );
}
} catch ( Exception e ) {
// An unexpected error occurred
result.setNrErrors( 1 );
logError( BaseMessages.getString( PKG, "JobMysqlBulkLoad.UnexpectedError.Label" ), e );
}
} else {
// No file was specified
result.setNrErrors( 1 );
logError( BaseMessages.getString( PKG, "JobMysqlBulkLoad.Nofilename.Label" ) );
}
return result;
}
public DatabaseMeta[] getUsedDatabaseConnections() {
return new DatabaseMeta[] { connection, };
}
public boolean isReplacedata() {
return replacedata;
}
public void setReplacedata( boolean replacedata ) {
this.replacedata = replacedata;
}
public void setLocalInfile( boolean localinfile ) {
this.localinfile = localinfile;
}
public boolean isLocalInfile() {
return localinfile;
}
public void setFilename( String filename ) {
this.filename = filename;
}
public String getFilename() {
return filename;
}
public void setSeparator( String separator ) {
this.separator = separator;
}
public void setLineterminated( String lineterminated ) {
this.lineterminated = lineterminated;
}
public void setLinestarted( String linestarted ) {
this.linestarted = linestarted;
}
public String getEnclosed() {
return enclosed;
}
public String getRealEnclosed() {
return environmentSubstitute( getEnclosed() );
}
public void setEnclosed( String enclosed ) {
this.enclosed = enclosed;
}
public String getEscaped() {
return escaped;
}
public String getRealEscaped() {
return environmentSubstitute( getEscaped() );
}
public void setEscaped( String escaped ) {
this.escaped = escaped;
}
public String getSeparator() {
return separator;
}
public String getLineterminated() {
return lineterminated;
}
public String getLinestarted() {
return linestarted;
}
public String getRealLinestarted() {
return environmentSubstitute( getLinestarted() );
}
public String getRealLineterminated() {
return environmentSubstitute( getLineterminated() );
}
public String getRealSeparator() {
return environmentSubstitute( getSeparator() );
}
public void setIgnorelines( String ignorelines ) {
this.ignorelines = ignorelines;
}
public String getIgnorelines() {
return ignorelines;
}
public String getRealIgnorelines() {
return environmentSubstitute( getIgnorelines() );
}
public void setListattribut( String listattribut ) {
this.listattribut = listattribut;
}
public String getListattribut() {
return listattribut;
}
public String getRealListattribut() {
return environmentSubstitute( getListattribut() );
}
public void setAddFileToResult( boolean addfiletoresultin ) {
this.addfiletoresult = addfiletoresultin;
}
public boolean isAddFileToResult() {
return addfiletoresult;
}
private String MysqlString( String listcolumns ) {
/*
* Handle forbiden char like '
*/
String returnString = "";
String[] split = listcolumns.split( "," );
for ( int i = 0; i < split.length; i++ ) {
if ( returnString.equals( "" ) ) {
returnString = "`" + Const.trim( split[i] ) + "`";
} else {
returnString = returnString + ", `" + Const.trim( split[i] ) + "`";
}
}
return returnString;
}
public List<ResourceReference> getResourceDependencies( JobMeta jobMeta ) {
List<ResourceReference> references = super.getResourceDependencies( jobMeta );
ResourceReference reference = null;
if ( connection != null ) {
reference = new ResourceReference( this );
references.add( reference );
reference.getEntries().add( new ResourceEntry( connection.getHostname(), ResourceType.SERVER ) );
reference.getEntries().add( new ResourceEntry( connection.getDatabaseName(), ResourceType.DATABASENAME ) );
}
if ( filename != null ) {
String realFilename = getRealFilename();
if ( reference == null ) {
reference = new ResourceReference( this );
references.add( reference );
}
reference.getEntries().add( new ResourceEntry( realFilename, ResourceType.FILE ) );
}
return references;
}
@Override
public void check( List<CheckResultInterface> remarks, JobMeta jobMeta, VariableSpace space,
Repository repository, IMetaStore metaStore ) {
ValidatorContext ctx = new ValidatorContext();
AbstractFileValidator.putVariableSpace( ctx, getVariables() );
AndValidator.putValidators( ctx, JobEntryValidatorUtils.notBlankValidator(), JobEntryValidatorUtils.fileExistsValidator() );
JobEntryValidatorUtils.andValidator().validate( this, "filename", remarks, ctx );
JobEntryValidatorUtils.andValidator().validate( this, "tablename", remarks,
AndValidator.putValidators( JobEntryValidatorUtils.notBlankValidator() ) );
}
}