This is an automated email from the ASF dual-hosted git repository.
hansva pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/hop.git
The following commit(s) were added to refs/heads/main by this push:
new baa3ad4cbe Cleanup XML of transform MonetDB Bulk Loader, fixes #1954
(#6772)
baa3ad4cbe is described below
commit baa3ad4cbeab491dcf7274aaf1d3e9b72f0666d1
Author: Hans Van Akelyen <[email protected]>
AuthorDate: Thu Mar 12 11:08:13 2026 +0100
Cleanup XML of transform MonetDB Bulk Loader, fixes #1954 (#6772)
---
.../monetdbbulkloader/MonetDbBulkLoader.java | 70 ++-
.../monetdbbulkloader/MonetDbBulkLoaderDialog.java | 45 +-
.../monetdbbulkloader/MonetDbBulkLoaderMeta.java | 467 +++++----------------
.../MonetDbBulkLoaderMetaTest.java | 153 +++++++
.../monetdbbulkloader/MonetDbBulkLoaderTest.java | 107 +++++
.../monetdbbulkloader/MonetDbVersionTest.java | 108 +++++
.../test/resources/monetdbbulkloader-transform.xml | 57 +++
7 files changed, 570 insertions(+), 437 deletions(-)
diff --git
a/plugins/transforms/monetdbbulkloader/src/main/java/org/apache/hop/pipeline/transforms/monetdbbulkloader/MonetDbBulkLoader.java
b/plugins/transforms/monetdbbulkloader/src/main/java/org/apache/hop/pipeline/transforms/monetdbbulkloader/MonetDbBulkLoader.java
index 14d48d3d3c..fd54cb56cb 100644
---
a/plugins/transforms/monetdbbulkloader/src/main/java/org/apache/hop/pipeline/transforms/monetdbbulkloader/MonetDbBulkLoader.java
+++
b/plugins/transforms/monetdbbulkloader/src/main/java/org/apache/hop/pipeline/transforms/monetdbbulkloader/MonetDbBulkLoader.java
@@ -50,6 +50,7 @@ public class MonetDbBulkLoader extends
BaseTransform<MonetDbBulkLoaderMeta, Mone
private IRowMeta physicalTableRowMeta;
private static final String ERROR_LOADING_DATA = "Error loading data: ";
+ DatabaseMeta databaseMeta;
public MonetDbBulkLoader(
TransformMeta transformMeta,
@@ -88,10 +89,8 @@ public class MonetDbBulkLoader extends
BaseTransform<MonetDbBulkLoaderMeta, Mone
logDetailed("Auto String Length flag: " + meta.isAutoStringWidths());
}
- DatabaseMeta dm = meta.getDatabaseMeta();
-
- String user = resolve(Const.NVL(dm.getUsername(), ""));
- String password = Utils.resolvePassword(variables,
Const.NVL(dm.getPassword(), ""));
+ String user = resolve(Const.NVL(databaseMeta.getUsername(), ""));
+ String password = Utils.resolvePassword(variables,
Const.NVL(databaseMeta.getPassword(), ""));
MapiSocket mserver = getMonetDBConnection();
data.mserver = mserver;
@@ -115,7 +114,7 @@ public class MonetDbBulkLoader extends
BaseTransform<MonetDbBulkLoaderMeta, Mone
// get table metadata, will be used later for date type identification
(DATE, TIMESTAMP, ...)
try {
- db = new Database(meta.getParent(), variables, dm);
+ db = new Database(meta.getParent(), variables, databaseMeta);
db.connect();
physicalTableRowMeta = db.getTableFields(data.schemaTable);
} catch (Exception e) {
@@ -149,7 +148,7 @@ public class MonetDbBulkLoader extends
BaseTransform<MonetDbBulkLoaderMeta, Mone
setOutputDone();
if (!first) {
try {
- writeBufferToMonetDB(meta.getDatabaseMeta());
+ writeBufferToMonetDB(databaseMeta);
data.out.flush();
} finally {
data.mserver.close();
@@ -163,9 +162,9 @@ public class MonetDbBulkLoader extends
BaseTransform<MonetDbBulkLoaderMeta, Mone
// Cache field indexes.
//
- data.keynrs = new int[meta.getFieldStream().length];
+ data.keynrs = new int[meta.getFields().size()];
for (int i = 0; i < data.keynrs.length; i++) {
- data.keynrs[i] =
getInputRowMeta().indexOfValue(meta.getFieldStream()[i]);
+ data.keynrs[i] =
getInputRowMeta().indexOfValue(meta.getFields().get(i).getFieldStream());
}
// execute the psql statement...
@@ -173,7 +172,7 @@ public class MonetDbBulkLoader extends
BaseTransform<MonetDbBulkLoaderMeta, Mone
execute(meta);
}
- writeRowToMonetDB(getInputRowMeta(), r, meta.getDatabaseMeta());
+ writeRowToMonetDB(getInputRowMeta(), r, databaseMeta);
putRow(getInputRowMeta(), r);
incrementLinesOutput();
@@ -240,7 +239,7 @@ public class MonetDbBulkLoader extends
BaseTransform<MonetDbBulkLoaderMeta, Mone
line.append(data.quote);
break;
case IValueMeta.TYPE_INTEGER:
- if (valueMeta.isStorageBinaryString() &&
meta.getFieldFormatOk()[i]) {
+ if (valueMeta.isStorageBinaryString() &&
meta.getFields().get(i).isFieldFormatOk()) {
line.append(valueMeta.getString(valueData));
} else {
Long value = valueMeta.getInteger(valueData);
@@ -257,7 +256,7 @@ public class MonetDbBulkLoader extends
BaseTransform<MonetDbBulkLoaderMeta, Mone
//
case IValueMeta.TYPE_TIMESTAMP, IValueMeta.TYPE_DATE:
// Keep the data format as indicated.
- if (valueMeta.isStorageBinaryString() &&
meta.getFieldFormatOk()[i]) {
+ if (valueMeta.isStorageBinaryString() &&
meta.getFields().get(i).isFieldFormatOk()) {
line.append(valueMeta.getString(valueData));
} else {
@@ -301,7 +300,7 @@ public class MonetDbBulkLoader extends
BaseTransform<MonetDbBulkLoaderMeta, Mone
break;
case IValueMeta.TYPE_NUMBER:
- if (valueMeta.isStorageBinaryString() &&
meta.getFieldFormatOk()[i]) {
+ if (valueMeta.isStorageBinaryString() &&
meta.getFields().get(i).isFieldFormatOk()) {
line.append(valueMeta.getString(valueData));
} else {
Double dbl = valueMeta.getNumber(valueData);
@@ -326,7 +325,7 @@ public class MonetDbBulkLoader extends
BaseTransform<MonetDbBulkLoaderMeta, Mone
break;
case IValueMeta.TYPE_BIGNUMBER:
- if (valueMeta.isStorageBinaryString() &&
meta.getFieldFormatOk()[i]) {
+ if (valueMeta.isStorageBinaryString() &&
meta.getFields().get(i).isFieldFormatOk()) {
line.append(valueMeta.getString(valueData));
} else {
BigDecimal bd = valueMeta.getBigNumber(valueData);
@@ -374,8 +373,8 @@ public class MonetDbBulkLoader extends
BaseTransform<MonetDbBulkLoaderMeta, Mone
String cmd;
String table = data.schemaTable;
String truncateStatement =
- meta.getDatabaseMeta()
- .getTruncateTableStatement(variables, meta.getSchemaName(),
meta.getTableName());
+ databaseMeta.getTruncateTableStatement(
+ variables, meta.getSchemaName(), meta.getTableName());
if (truncateStatement == null) {
throw new HopException("Truncate table is not supported!");
}
@@ -535,7 +534,7 @@ public class MonetDbBulkLoader extends
BaseTransform<MonetDbBulkLoaderMeta, Mone
protected void verifyDatabaseConnection() throws HopException {
// Confirming Database Connection is defined.
- if (meta.getDatabaseMeta() == null) {
+ if (databaseMeta == null) {
throw new HopException(
BaseMessages.getString(PKG,
"MonetDbBulkLoaderMeta.GetSQL.NoConnectionDefined"));
}
@@ -545,6 +544,8 @@ public class MonetDbBulkLoader extends
BaseTransform<MonetDbBulkLoaderMeta, Mone
public boolean init() {
if (super.init()) {
+ databaseMeta =
getPipelineMeta().findDatabase(meta.getDbConnectionName(), variables);
+
try {
verifyDatabaseConnection();
} catch (HopException ex) {
@@ -592,21 +593,12 @@ public class MonetDbBulkLoader extends
BaseTransform<MonetDbBulkLoaderMeta, Mone
// Make sure our database connection settings are consistent with our
dialog settings by
// altering the connection with an updated answer depending on the
dialog setting.
- meta.getDatabaseMeta().setQuoteAllFields(meta.isFullyQuoteSQL());
-
- // Support parameterized database connection names
- String connectionName = meta.getDbConnectionName();
- if (!Utils.isEmpty(connectionName)
- && connectionName.startsWith("${")
- && connectionName.endsWith("}")) {
-
meta.setDatabaseMeta(getPipelineMeta().findDatabase(resolve(connectionName),
variables));
- }
+ databaseMeta.setQuoteAllFields(meta.isFullyQuoteSQL());
// Schema-table combination...
data.schemaTable =
- meta.getDatabaseMeta(this)
- .getQuotedSchemaTableCombination(
- variables, meta.getSchemaName(), meta.getTableName());
+ databaseMeta.getQuotedSchemaTableCombination(
+ variables, meta.getSchemaName(), meta.getTableName());
return true;
}
@@ -622,12 +614,11 @@ public class MonetDbBulkLoader extends
BaseTransform<MonetDbBulkLoaderMeta, Mone
if (this.meta == null) {
throw new HopException("No metadata available to determine connection
information from.");
}
- DatabaseMeta dm = meta.getDatabaseMeta();
- String hostname = resolve(Const.NVL(dm.getHostname(), ""));
- String portnum = resolve(Const.NVL(dm.getPort(), ""));
- String user = resolve(Const.NVL(dm.getUsername(), ""));
- String password = Utils.resolvePassword(variables,
Const.NVL(dm.getPassword(), ""));
- String db = resolve(Const.NVL(dm.getDatabaseName(), ""));
+ String hostname = resolve(Const.NVL(databaseMeta.getHostname(), ""));
+ String portnum = resolve(Const.NVL(databaseMeta.getPort(), ""));
+ String user = resolve(Const.NVL(databaseMeta.getUsername(), ""));
+ String password = Utils.resolvePassword(variables,
Const.NVL(databaseMeta.getPassword(), ""));
+ String db = resolve(Const.NVL(databaseMeta.getDatabaseName(), ""));
return getMonetDBConnection(
hostname, Integer.parseInt(portnum), user, password, db,
getLogChannel());
@@ -664,12 +655,11 @@ public class MonetDbBulkLoader extends
BaseTransform<MonetDbBulkLoaderMeta, Mone
if (this.meta == null) {
throw new HopException("No metadata available to determine connection
information from.");
}
- DatabaseMeta dm = meta.getDatabaseMeta();
- String hostname = resolve(Const.NVL(dm.getHostname(), ""));
- String portnum = resolve(Const.NVL(dm.getPort(), ""));
- String user = resolve(Const.NVL(dm.getUsername(), ""));
- String password = Utils.resolvePassword(variables,
Const.NVL(dm.getPassword(), ""));
- String db = resolve(Const.NVL(dm.getDatabaseName(), ""));
+ String hostname = resolve(Const.NVL(databaseMeta.getHostname(), ""));
+ String portnum = resolve(Const.NVL(databaseMeta.getPort(), ""));
+ String user = resolve(Const.NVL(databaseMeta.getUsername(), ""));
+ String password = Utils.resolvePassword(variables,
Const.NVL(databaseMeta.getPassword(), ""));
+ String db = resolve(Const.NVL(databaseMeta.getDatabaseName(), ""));
executeSql(query, hostname, Integer.parseInt(portnum), user, password, db);
}
diff --git
a/plugins/transforms/monetdbbulkloader/src/main/java/org/apache/hop/pipeline/transforms/monetdbbulkloader/MonetDbBulkLoaderDialog.java
b/plugins/transforms/monetdbbulkloader/src/main/java/org/apache/hop/pipeline/transforms/monetdbbulkloader/MonetDbBulkLoaderDialog.java
index 4ca17e8be3..7b4e1b6bc2 100644
---
a/plugins/transforms/monetdbbulkloader/src/main/java/org/apache/hop/pipeline/transforms/monetdbbulkloader/MonetDbBulkLoaderDialog.java
+++
b/plugins/transforms/monetdbbulkloader/src/main/java/org/apache/hop/pipeline/transforms/monetdbbulkloader/MonetDbBulkLoaderDialog.java
@@ -163,7 +163,7 @@ public class MonetDbBulkLoaderDialog extends
BaseTransformDialog {
// Connection line
//
// Connection line
- wConnection = addConnectionLine(shell, wSpacer, input.getDatabaseMeta(),
lsMod);
+ wConnection = addConnectionLine(shell, wSpacer,
input.getDbConnectionName(), lsMod);
// //////////////////////////////////////////////
// Prepare the Folder that will contain tabs. //
@@ -328,12 +328,13 @@ public class MonetDbBulkLoaderDialog extends
BaseTransformDialog {
wFullyQuoteSQL = new Button(wGeneralSettingsComp, SWT.CHECK);
PropsUi.setLook(wFullyQuoteSQL);
+ DatabaseMeta databaseMeta =
pipelineMeta.findDatabase(wConnection.getText(), variables);
SelectionAdapter lsFullyQuoteSQL =
new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent arg0) {
input.setChanged();
-
input.getDatabaseMeta().setQuoteAllFields(wFullyQuoteSQL.getSelection());
+ databaseMeta.setQuoteAllFields(wFullyQuoteSQL.getSelection());
}
};
wFullyQuoteSQL.addSelectionListener(lsFullyQuoteSQL);
@@ -408,7 +409,7 @@ public class MonetDbBulkLoaderDialog extends
BaseTransformDialog {
PropsUi.setLook(wlReturn);
int upInsCols = 3;
- int upInsRows = (input.getFieldTable() != null ?
input.getFieldTable().length : 1);
+ int upInsRows = (input.getFields() != null ? input.getFields().size() : 1);
ciReturn = new ColumnInfo[upInsCols];
ciReturn[0] =
@@ -784,21 +785,21 @@ public class MonetDbBulkLoaderDialog extends
BaseTransformDialog {
logDebug(BaseMessages.getString(PKG,
"MonetDBBulkLoaderDialog.Log.GettingKeyInfo"));
}
- if (input.getFieldTable() != null) {
- for (int i = 0; i < input.getFieldTable().length; i++) {
+ if (!input.getFields().isEmpty()) {
+ for (int i = 0; i < input.getFields().size(); i++) {
TableItem item = wReturn.table.getItem(i);
- if (input.getFieldTable()[i] != null) {
- item.setText(1, input.getFieldTable()[i]);
+ if (input.getFields().get(i).getFieldTable() != null) {
+ item.setText(1, input.getFields().get(i).getFieldTable());
}
- if (input.getFieldStream()[i] != null) {
- item.setText(2, input.getFieldStream()[i]);
+ if (input.getFields().get(i).getFieldStream() != null) {
+ item.setText(2, input.getFields().get(i).getFieldStream());
}
- item.setText(3, input.getFieldFormatOk()[i] ? "Y" : "N");
+ item.setText(3, input.getFields().get(i).isFieldFormatOk() ? "Y" :
"N");
}
}
- if (input.getDatabaseMeta() != null) {
- wConnection.setText(input.getDatabaseMeta().getName());
+ if (input.getDbConnectionName() != null) {
+ wConnection.setText(input.getDbConnectionName());
}
// General Settings Tab values from transform meta-data configuration.
if (input.getSchemaName() != null) {
@@ -855,24 +856,23 @@ public class MonetDbBulkLoaderDialog extends
BaseTransformDialog {
protected void getInfo(MonetDbBulkLoaderMeta inf) {
int nrfields = wReturn.nrNonEmpty();
- inf.allocate(nrfields);
-
if (log.isDebug()) {
logDebug(
BaseMessages.getString(PKG,
"MonetDBBulkLoaderDialog.Log.FoundFields", "" + nrfields));
}
+ inf.getFields().clear();
for (int i = 0; i < nrfields; i++) {
TableItem item = wReturn.getNonEmpty(i);
- inf.getFieldTable()[i] = item.getText(1);
- inf.getFieldStream()[i] = item.getText(2);
- inf.getFieldFormatOk()[i] = "Y".equalsIgnoreCase(item.getText(3));
+ MonetDbBulkLoaderMeta.MonetDbField mf =
+ new MonetDbBulkLoaderMeta.MonetDbField(
+ item.getText(1), item.getText(2),
"Y".equalsIgnoreCase(item.getText(3)));
+ inf.getFields().add(mf);
}
// General Settings Tab values from transform meta-data configuration.
inf.setDbConnectionName(wConnection.getText());
inf.setSchemaName(wSchema.getText());
inf.setTableName(wTable.getText());
- inf.setDatabaseMeta(pipelineMeta.findDatabase(wConnection.getText(),
variables));
inf.setBufferSize(wBufferSize.getText());
inf.setLogFile(wLogFile.getText());
inf.setTruncate(wTruncate.getSelection());
@@ -954,7 +954,6 @@ public class MonetDbBulkLoaderDialog extends
BaseTransformDialog {
return;
}
// refresh data
- input.setDatabaseMeta(pipelineMeta.findDatabase(wConnection.getText(),
variables));
input.setTableName(variables.resolve(wTable.getText()));
ITransformMeta transformMetaInterface = transformMeta.getTransform();
try {
@@ -1118,17 +1117,13 @@ public class MonetDbBulkLoaderDialog extends
BaseTransformDialog {
String name = transformName; // new name might not yet be linked to
other transforms!
+ DatabaseMeta databaseMeta =
pipelineMeta.findDatabase(wConnection.getText(), variables);
SqlStatement sql = info.getTableDdl(variables, pipelineMeta, name,
false, null, false);
if (!sql.hasError()) {
if (sql.hasSql()) {
SqlEditor sqledit =
new SqlEditor(
- shell,
- SWT.NONE,
- variables,
- info.getDatabaseMeta(),
- DbCache.getInstance(),
- sql.getSql());
+ shell, SWT.NONE, variables, databaseMeta,
DbCache.getInstance(), sql.getSql());
sqledit.open();
} else {
MessageBox mb = new MessageBox(shell, SWT.OK | SWT.ICON_INFORMATION);
diff --git
a/plugins/transforms/monetdbbulkloader/src/main/java/org/apache/hop/pipeline/transforms/monetdbbulkloader/MonetDbBulkLoaderMeta.java
b/plugins/transforms/monetdbbulkloader/src/main/java/org/apache/hop/pipeline/transforms/monetdbbulkloader/MonetDbBulkLoaderMeta.java
index 0190feddcd..da14c5142f 100644
---
a/plugins/transforms/monetdbbulkloader/src/main/java/org/apache/hop/pipeline/transforms/monetdbbulkloader/MonetDbBulkLoaderMeta.java
+++
b/plugins/transforms/monetdbbulkloader/src/main/java/org/apache/hop/pipeline/transforms/monetdbbulkloader/MonetDbBulkLoaderMeta.java
@@ -16,7 +16,10 @@
*/
package org.apache.hop.pipeline.transforms.monetdbbulkloader;
+import java.util.ArrayList;
import java.util.List;
+import lombok.Getter;
+import lombok.Setter;
import org.apache.hop.core.CheckResult;
import org.apache.hop.core.Const;
import org.apache.hop.core.ICheckResult;
@@ -27,23 +30,20 @@ import org.apache.hop.core.database.Database;
import org.apache.hop.core.database.DatabaseMeta;
import org.apache.hop.core.exception.HopException;
import org.apache.hop.core.exception.HopTransformException;
-import org.apache.hop.core.exception.HopXmlException;
-import org.apache.hop.core.injection.Injection;
-import org.apache.hop.core.injection.InjectionSupported;
import org.apache.hop.core.row.IRowMeta;
import org.apache.hop.core.row.IValueMeta;
import org.apache.hop.core.row.RowMeta;
import org.apache.hop.core.util.Utils;
import org.apache.hop.core.variables.IVariables;
-import org.apache.hop.core.xml.XmlHandler;
import org.apache.hop.databases.monetdb.MonetDBDatabaseMeta;
import org.apache.hop.i18n.BaseMessages;
+import org.apache.hop.metadata.api.HopMetadataProperty;
+import org.apache.hop.metadata.api.HopMetadataPropertyType;
import org.apache.hop.metadata.api.IHopMetadataProvider;
import org.apache.hop.pipeline.DatabaseImpact;
import org.apache.hop.pipeline.PipelineMeta;
import org.apache.hop.pipeline.transform.BaseTransformMeta;
import org.apache.hop.pipeline.transform.TransformMeta;
-import org.w3c.dom.Node;
@Transform(
id = "MonetDBBulkLoader",
@@ -56,53 +56,69 @@ import org.w3c.dom.Node;
isIncludeJdbcDrivers = true,
classLoaderGroup = "monetdb",
actionTransformTypes = {ActionTransformType.RDBMS,
ActionTransformType.OUTPUT})
-@InjectionSupported(localizationPrefix = "MonetDBBulkLoaderDialog.Injection.")
+@Getter
+@Setter
public class MonetDbBulkLoaderMeta
extends BaseTransformMeta<MonetDbBulkLoader, MonetDbBulkLoaderData> {
private static final Class<?> PKG =
MonetDbBulkLoaderMeta.class; // for i18n purposes, needed by
Translator2!!
public static final String CONST_SPACES = " ";
+ /** Inline class representing a single field mapping from stream to table
column. */
+ @Getter
+ @Setter
+ public static class MonetDbField {
+ @HopMetadataProperty(key = "stream_name", injectionKey = "TARGETFIELDS")
+ private String fieldTable;
+
+ @HopMetadataProperty(key = "field_name", injectionKey = "SOURCEFIELDS")
+ private String fieldStream;
+
+ @HopMetadataProperty(key = "field_format_ok", injectionKey =
"FIELDFORMATOK")
+ private boolean fieldFormatOk;
+
+ public MonetDbField() {}
+
+ public MonetDbField(String fieldTable, String fieldStream, boolean
fieldFormatOk) {
+ this.fieldTable = fieldTable;
+ this.fieldStream = fieldStream;
+ this.fieldFormatOk = fieldFormatOk;
+ }
+ }
+
/** The database connection name * */
- @Injection(name = "CONNECTIONNAME")
+ @HopMetadataProperty(
+ key = "connection",
+ injectionKey = "CONNECTIONNAME",
+ hopMetadataPropertyType = HopMetadataPropertyType.RDBMS_CONNECTION)
private String dbConnectionName;
/** what's the schema for the target? */
- @Injection(name = "SCHEMANAME")
+ @HopMetadataProperty(key = "schema", injectionKey = "SCHEMANAME")
private String schemaName;
/** what's the table for the target? */
- @Injection(name = "TABLENAME")
+ @HopMetadataProperty(key = "table", injectionKey = "SCHEMANAME")
private String tableName;
/** Path to the log file */
- @Injection(name = "LOGFILE")
+ @HopMetadataProperty(key = "log_file", injectionKey = "LOGFILE")
private String logFile;
- /** database connection */
- private DatabaseMeta databaseMeta;
-
- /** Field name of the target table */
- @Injection(name = "TARGETFIELDS")
- private String[] fieldTable;
-
- /** Field name in the stream */
- @Injection(name = "SOURCEFIELDS")
- private String[] fieldStream;
-
- /** flag to indicate that the format is OK for MonetDB */
- @Injection(name = "FIELDFORMATOK")
- private boolean[] fieldFormatOk;
+ @HopMetadataProperty(
+ key = "mapping",
+ inlineListTags = {"stream_name", "field_name", "field_format_ok"})
+ private List<MonetDbField> fields;
/** Field separator character or string used to delimit fields */
- @Injection(name = "SEPARATOR")
+ @HopMetadataProperty(key = "field_separator", injectionKey = "SEPARATOR")
private String fieldSeparator;
/**
* Specifies which character surrounds each field's data. i.e. double
quotes, single quotes or
* something else
*/
- @Injection(name = "FIELDENCLOSURE")
+ @HopMetadataProperty(key = "field_enclosure", injectionKey =
"FIELDENCLOSURE")
private String fieldEnclosure;
/**
@@ -110,19 +126,19 @@ public class MonetDbBulkLoaderMeta
* something else the value is written out as text to the API and MonetDB is
able to interpret it
* to the correct representation of NULL in the database for the given
column type.
*/
- @Injection(name = "NULLVALUE")
+ @HopMetadataProperty(key = "null_representation", injectionKey = "NULLVALUE")
private String nullRepresentation;
/** Encoding to use */
- @Injection(name = "ENCODING")
+ @HopMetadataProperty(key = "encoding", injectionKey = "ENCODING")
private String encoding;
/** Truncate table? */
- @Injection(name = "TRUNCATE")
+ @HopMetadataProperty(key = "truncate", injectionKey = "TRUNCATE")
private boolean truncate = false;
/** Fully Quote SQL used in the transform? */
- @Injection(name = "QUOTEFIELDS")
+ @HopMetadataProperty(key = "fully_quote_sql", injectionKey = "QUOTEFIELDS")
private boolean fullyQuoteSQL;
/** Auto adjust the table structure? */
@@ -131,38 +147,11 @@ public class MonetDbBulkLoaderMeta
/** Auto adjust strings that are too long? */
private boolean autoStringWidths = false;
- public boolean isAutoStringWidths() {
- return autoStringWidths;
- }
-
- public boolean isTruncate() {
- return truncate;
- }
-
- public void setTruncate(boolean truncate) {
- this.truncate = truncate;
- }
-
- public boolean isFullyQuoteSQL() {
- return fullyQuoteSQL;
- }
-
- public void setFullyQuoteSQL(boolean fullyQuoteSQLbool) {
- this.fullyQuoteSQL = fullyQuoteSQLbool;
- }
-
- public boolean isAutoSchema() {
- return autoSchema;
- }
-
- public void setAutoSchema(boolean autoSchema) {
- this.autoSchema = autoSchema;
- }
-
/**
* The number of rows to buffer before passing them over to MonetDB. This
number should be
* non-zero since we need to specify the number of rows we pass.
*/
+ @HopMetadataProperty(key = "buffer_size")
private String bufferSize;
/**
@@ -172,215 +161,19 @@ public class MonetDbBulkLoaderMeta
*/
private boolean compatibilityDbVersionMode = false;
- /**
- * @return Returns the database.
- */
- public DatabaseMeta getDatabaseMeta() {
- return databaseMeta;
- }
-
- /**
- * @return Returns the database.
- */
- public DatabaseMeta getDatabaseMeta(MonetDbBulkLoader loader) {
- return databaseMeta;
- }
-
- /**
- * @param database The database to set.
- */
- public void setDatabaseMeta(DatabaseMeta database) {
- this.databaseMeta = database;
- }
-
- /**
- * @return Returns the tableName.
- */
- public String getTableName() {
- return tableName;
- }
-
- /**
- * @param tableName The tableName to set.
- */
- public void setTableName(String tableName) {
- this.tableName = tableName;
- }
-
- /**
- * @return Returns the fieldTable.
- */
- public String[] getFieldTable() {
- return fieldTable;
- }
-
- /**
- * @param fieldTable The fieldTable to set.
- */
- public void setFieldTable(String[] fieldTable) {
- this.fieldTable = fieldTable;
- }
-
- /**
- * @return Returns the fieldStream.
- */
- public String[] getFieldStream() {
- return fieldStream;
- }
-
- /**
- * @param fieldStream The fieldStream to set.
- */
- public void setFieldStream(String[] fieldStream) {
- this.fieldStream = fieldStream;
- }
-
- /**
- * @deprecated
- * @param transformNode xml for the transform
- * @param metadataProvider containing variables
- * @throws HopXmlException when unable to parse xml
- */
- @Override
- @Deprecated(since = "2.14")
- public void loadXml(Node transformNode, IHopMetadataProvider
metadataProvider)
- throws HopXmlException {
- readData(transformNode, metadataProvider);
- }
-
- public void allocate(int nrvalues) {
- fieldTable = new String[nrvalues];
- fieldStream = new String[nrvalues];
- fieldFormatOk = new boolean[nrvalues];
- }
-
- @Override
- public Object clone() {
- MonetDbBulkLoaderMeta retval = (MonetDbBulkLoaderMeta) super.clone();
- int nrvalues = fieldTable.length;
-
- retval.allocate(nrvalues);
-
- System.arraycopy(fieldTable, 0, retval.fieldTable, 0, nrvalues);
- System.arraycopy(fieldStream, 0, retval.fieldStream, 0, nrvalues);
- System.arraycopy(fieldFormatOk, 0, retval.fieldFormatOk, 0, nrvalues);
- return retval;
- }
-
- private void readData(Node transformNode, IHopMetadataProvider
metadataProvider)
- throws HopXmlException {
- try {
- dbConnectionName = XmlHandler.getTagValue(transformNode, "connection");
- databaseMeta = DatabaseMeta.loadDatabase(metadataProvider,
dbConnectionName);
-
- schemaName = XmlHandler.getTagValue(transformNode, "schema");
- tableName = XmlHandler.getTagValue(transformNode, "table");
- bufferSize = XmlHandler.getTagValue(transformNode, "buffer_size");
- logFile = XmlHandler.getTagValue(transformNode, "log_file");
- truncate = "Y".equals(XmlHandler.getTagValue(transformNode, "truncate"));
-
- // New in January 2013 Updates - For compatibility we set default values
according to the old
- // version of the transform.
- //
-
- // This expression will only be true if a yes answer was previously
recorded.
- fullyQuoteSQL = "Y".equals(XmlHandler.getTagValue(transformNode,
"fully_quote_sql"));
-
- fieldSeparator = XmlHandler.getTagValue(transformNode,
"field_separator");
- if (fieldSeparator == null) {
- fieldSeparator = "|";
- }
- fieldEnclosure = XmlHandler.getTagValue(transformNode,
"field_enclosure");
- if (fieldEnclosure == null) {
- fieldEnclosure = "\"";
- }
- nullRepresentation = XmlHandler.getTagValue(transformNode,
"null_representation");
- if (nullRepresentation == null) {
- nullRepresentation = "null";
- }
- encoding = XmlHandler.getTagValue(transformNode, "encoding");
- if (encoding == null) {
- encoding = "UTF-8";
- }
-
- int nrvalues = XmlHandler.countNodes(transformNode, "mapping");
- allocate(nrvalues);
-
- for (int i = 0; i < nrvalues; i++) {
- Node vnode = XmlHandler.getSubNodeByNr(transformNode, "mapping", i);
-
- fieldTable[i] = XmlHandler.getTagValue(vnode, "stream_name");
- fieldStream[i] = XmlHandler.getTagValue(vnode, "field_name");
- if (fieldStream[i] == null) {
- fieldStream[i] = fieldTable[i]; // default: the same name!
- }
- fieldFormatOk[i] = "Y".equalsIgnoreCase(XmlHandler.getTagValue(vnode,
"field_format_ok"));
- }
- } catch (Exception e) {
- throw new HopXmlException(
- BaseMessages.getString(
- PKG,
"MonetDBBulkLoaderMeta.Exception.UnableToReadTransformInfoFromXML"),
- e);
- }
- }
-
@Override
public void setDefault() {
- fieldTable = null;
- databaseMeta = null;
+ fields = new ArrayList<>();
schemaName = "";
tableName = BaseMessages.getString(PKG,
"MonetDBBulkLoaderMeta.DefaultTableName");
bufferSize = "100000";
logFile = "";
truncate = false;
fullyQuoteSQL = true;
-
- // MonetDB safe defaults.
fieldSeparator = "|";
fieldEnclosure = "\"";
nullRepresentation = "";
encoding = "UTF-8";
- allocate(0);
- }
-
- /**
- * @deprecated
- * @return the XML to store the transform
- */
- @Override
- @Deprecated(since = "2.14")
- public String getXml() {
- StringBuilder retval = new StringBuilder(300);
-
- // General Settings Tab
- retval.append(" ").append(XmlHandler.addTagValue("connection",
dbConnectionName));
- retval.append(" ").append(XmlHandler.addTagValue("buffer_size",
bufferSize));
- retval.append(" ").append(XmlHandler.addTagValue("schema", schemaName));
- retval.append(" ").append(XmlHandler.addTagValue("table", tableName));
- retval.append(" ").append(XmlHandler.addTagValue("log_file", logFile));
- retval.append(" ").append(XmlHandler.addTagValue("truncate", truncate));
- retval.append(" ").append(XmlHandler.addTagValue("fully_quote_sql",
fullyQuoteSQL));
-
- // MonetDB Settings Tab
- retval.append(" ").append(XmlHandler.addTagValue("field_separator",
fieldSeparator));
- retval.append(" ").append(XmlHandler.addTagValue("field_enclosure",
fieldEnclosure));
- retval.append(" ").append(XmlHandler.addTagValue("null_representation",
nullRepresentation));
- retval.append(" ").append(XmlHandler.addTagValue("encoding", encoding));
-
- // Output Fields Tab
- for (int i = 0; i < fieldTable.length; i++) {
- Boolean fieldFormat = false;
- if (fieldFormatOk.length == fieldTable.length) {
- fieldFormat = fieldFormatOk[i];
- }
- retval.append(" <mapping>").append(Const.CR);
- retval.append(CONST_SPACES).append(XmlHandler.addTagValue("stream_name",
fieldTable[i]));
- retval.append(CONST_SPACES).append(XmlHandler.addTagValue("field_name",
fieldStream[i]));
-
retval.append(CONST_SPACES).append(XmlHandler.addTagValue("field_format_ok",
fieldFormat));
- retval.append(" </mapping>").append(Const.CR);
- }
-
- return retval.toString();
}
@Override
@@ -409,6 +202,25 @@ public class MonetDbBulkLoaderMeta
CheckResult cr;
StringBuilder erroMessage = new StringBuilder();
+ DatabaseMeta databaseMeta = null;
+
+ try {
+ databaseMeta =
+ metadataProvider
+ .getSerializer(DatabaseMeta.class)
+ .load(variables.resolve(dbConnectionName));
+ } catch (HopException e) {
+ cr =
+ new CheckResult(
+ ICheckResult.TYPE_RESULT_ERROR,
+ BaseMessages.getString(
+ PKG,
+ "TableInputMeta.CheckResult.DatabaseMetaError",
+ variables.resolve(dbConnectionName)),
+ transformMeta);
+ remarks.add(cr);
+ }
+
if (databaseMeta != null) {
Database db = new Database(loggingObject, variables, databaseMeta);
try {
@@ -441,8 +253,8 @@ public class MonetDbBulkLoaderMeta
first = true;
errorFound = false;
- for (String field : fieldTable) {
- IValueMeta v = r.searchValueMeta(field);
+ for (MonetDbField field : fields) {
+ IValueMeta v = r.searchValueMeta(field.fieldTable);
if (v == null) {
if (first) {
first = false;
@@ -496,8 +308,8 @@ public class MonetDbBulkLoaderMeta
erroMessage.setLength(0);
boolean errorFound = false;
- for (String s : fieldStream) {
- IValueMeta v = prev.searchValueMeta(s);
+ for (MonetDbField s : fields) {
+ IValueMeta v = prev.searchValueMeta(s.fieldStream);
if (v == null) {
if (first) {
first = false;
@@ -608,21 +420,16 @@ public class MonetDbBulkLoaderMeta
public IRowMeta updateFields(IRowMeta prev, MonetDbBulkLoaderData data) {
// update the field table from the fields coming from the previous
transforms
IRowMeta tableFields = new RowMeta();
- List<IValueMeta> fields = prev.getValueMetaList();
- fieldTable = new String[fields.size()];
- fieldStream = new String[fields.size()];
- fieldFormatOk = new boolean[fields.size()];
+ List<IValueMeta> prevFields = prev.getValueMetaList();
int idx = 0;
- for (IValueMeta field : fields) {
+ for (IValueMeta field : prevFields) {
IValueMeta tableField = field.clone();
tableFields.addValueMeta(tableField);
- fieldTable[idx] = field.getName();
- fieldStream[idx] = field.getName();
- fieldFormatOk[idx] = true;
+ fields.add(new MonetDbField(field.getName(), field.getName(), true));
idx++;
}
- data.keynrs = new int[getFieldStream().length];
+ data.keynrs = new int[fields.size()];
for (int i = 0; i < data.keynrs.length; i++) {
data.keynrs[i] = i;
}
@@ -636,6 +443,9 @@ public class MonetDbBulkLoaderMeta
boolean autoSchema,
MonetDbBulkLoaderData data,
boolean safeMode) {
+
+ DatabaseMeta databaseMeta =
+
getParentTransformMeta().getParentPipelineMeta().findDatabase(dbConnectionName,
variables);
SqlStatement retval =
new SqlStatement(transformMeta.getName(), databaseMeta, null); //
default: nothing to do!
@@ -649,11 +459,11 @@ public class MonetDbBulkLoaderMeta
} else {
tableFields = new RowMeta();
// Now change the field names
- for (int i = 0; i < fieldTable.length; i++) {
- IValueMeta v = prev.searchValueMeta(fieldStream[i]);
+ for (int i = 0; i < fields.size(); i++) {
+ IValueMeta v = prev.searchValueMeta(fields.get(i).fieldStream);
if (v != null) {
IValueMeta tableField = v.clone();
- tableField.setName(fieldTable[i]);
+ tableField.setName(fields.get(i).fieldTable);
tableFields.addValueMeta(tableField);
}
}
@@ -712,11 +522,14 @@ public class MonetDbBulkLoaderMeta
IRowMeta info,
IHopMetadataProvider metadataProvider)
throws HopTransformException {
+ DatabaseMeta databaseMeta =
+
getParentTransformMeta().getParentPipelineMeta().findDatabase(dbConnectionName,
variables);
+
if (prev != null) {
/* DEBUG CHECK THIS */
// Insert dateMask fields : read/write
- for (int i = 0; i < fieldTable.length; i++) {
- IValueMeta v = prev.searchValueMeta(fieldStream[i]);
+ for (MonetDbField field : fields) {
+ IValueMeta v = prev.searchValueMeta(field.fieldStream);
DatabaseImpact ii =
new DatabaseImpact(
@@ -725,8 +538,8 @@ public class MonetDbBulkLoaderMeta
transformMeta.getName(),
databaseMeta.getDatabaseName(),
variables.resolve(tableName),
- fieldTable[i],
- fieldStream[i],
+ field.fieldTable,
+ field.fieldStream,
v != null ? v.getOrigin() : "?",
"",
"Type = " + v.toStringMeta());
@@ -740,6 +553,9 @@ public class MonetDbBulkLoaderMeta
String realTableName = variables.resolve(tableName);
String realSchemaName = variables.resolve(schemaName);
+ DatabaseMeta databaseMeta =
+
getParentTransformMeta().getParentPipelineMeta().findDatabase(dbConnectionName,
variables);
+
if (databaseMeta != null) {
Database db = new Database(loggingObject, variables, databaseMeta);
try {
@@ -773,102 +589,6 @@ public class MonetDbBulkLoaderMeta
}
}
- /**
- * @return the schemaName
- */
- public String getSchemaName() {
- return schemaName;
- }
-
- /**
- * @param schemaName the schemaName to set
- */
- public void setSchemaName(String schemaName) {
- this.schemaName = schemaName;
- }
-
- public String getLogFile() {
- return logFile;
- }
-
- public void setLogFile(String logFile) {
- this.logFile = logFile;
- }
-
- public String getFieldSeparator() {
- return fieldSeparator;
- }
-
- public void setFieldSeparator(String fieldSeparatorStr) {
- this.fieldSeparator = fieldSeparatorStr;
- }
-
- public String getFieldEnclosure() {
- return fieldEnclosure;
- }
-
- public void setFieldEnclosure(String fieldEnclosureStr) {
- this.fieldEnclosure = fieldEnclosureStr;
- }
-
- public String getNullRepresentation() {
- return nullRepresentation;
- }
-
- public void setNullRepresentation(String nullRepresentationString) {
- this.nullRepresentation = nullRepresentationString;
- }
-
- public String getEncoding() {
- return encoding;
- }
-
- public void setEncoding(String encoding) {
- this.encoding = encoding;
- }
-
- /**
- * @return the bufferSize
- */
- public String getBufferSize() {
- return bufferSize;
- }
-
- /**
- * @param bufferSize the bufferSize to set
- */
- public void setBufferSize(String bufferSize) {
- this.bufferSize = bufferSize;
- }
-
- /**
- * @return the fieldFormatOk
- */
- public boolean[] getFieldFormatOk() {
- return fieldFormatOk;
- }
-
- /**
- * @param fieldFormatOk the fieldFormatOk to set
- */
- public void setFieldFormatOk(boolean[] fieldFormatOk) {
- this.fieldFormatOk = fieldFormatOk;
- }
-
- /**
- * @param dbConnectionName connection name to set
- */
- public void setDbConnectionName(String dbConnectionName) {
- this.dbConnectionName = dbConnectionName;
- }
-
- /**
- * @return the database connection name
- */
- public String getDbConnectionName() {
- return this.dbConnectionName;
- }
-
/**
* Returns the version of MonetDB that is used.
*
@@ -876,7 +596,10 @@ public class MonetDbBulkLoaderMeta
* @throws HopException if an error occurs
*/
private MonetDbVersion getMonetDBVersion(IVariables variables) throws
HopException {
- Database db = null;
+ Database db;
+
+ DatabaseMeta databaseMeta =
+
getParentTransformMeta().getParentPipelineMeta().findDatabase(dbConnectionName,
variables);
db = new Database(loggingObject, variables, databaseMeta);
try {
diff --git
a/plugins/transforms/monetdbbulkloader/src/test/java/org/apache/hop/pipeline/transforms/monetdbbulkloader/MonetDbBulkLoaderMetaTest.java
b/plugins/transforms/monetdbbulkloader/src/test/java/org/apache/hop/pipeline/transforms/monetdbbulkloader/MonetDbBulkLoaderMetaTest.java
new file mode 100644
index 0000000000..f36c118bd6
--- /dev/null
+++
b/plugins/transforms/monetdbbulkloader/src/test/java/org/apache/hop/pipeline/transforms/monetdbbulkloader/MonetDbBulkLoaderMetaTest.java
@@ -0,0 +1,153 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.hop.pipeline.transforms.monetdbbulkloader;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.apache.hop.core.HopEnvironment;
+import org.apache.hop.core.database.DatabaseMeta;
+import org.apache.hop.core.plugins.PluginRegistry;
+import org.apache.hop.core.row.IRowMeta;
+import org.apache.hop.core.row.RowMeta;
+import org.apache.hop.core.variables.Variables;
+import org.apache.hop.core.xml.XmlHandler;
+import org.apache.hop.metadata.api.IHopMetadataProvider;
+import org.apache.hop.metadata.serializer.memory.MemoryMetadataProvider;
+import org.apache.hop.pipeline.transform.TransformMeta;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+
+class MonetDbBulkLoaderMetaTest {
+
+ @BeforeEach
+ void setUp() throws Exception {
+ HopEnvironment.init();
+ PluginRegistry.init();
+ }
+
+ @Test
+ void testSerialization() throws Exception {
+ IHopMetadataProvider metadataProvider = new MemoryMetadataProvider();
+ DatabaseMeta dbMeta = new DatabaseMeta();
+ dbMeta.setName("unit-test-db");
+ metadataProvider.getSerializer(DatabaseMeta.class).save(dbMeta);
+
+ Document document =
+ XmlHandler.loadXmlFile(
+
MonetDbBulkLoaderMeta.class.getResourceAsStream("/monetdbbulkloader-transform.xml"));
+ Node node = XmlHandler.getSubNode(document, TransformMeta.XML_TAG);
+
+ MonetDbBulkLoaderMeta meta = new MonetDbBulkLoaderMeta();
+ meta.loadXml(node, metadataProvider);
+
+ String xml =
+ XmlHandler.openTag(TransformMeta.XML_TAG)
+ + meta.getXml()
+ + XmlHandler.closeTag(TransformMeta.XML_TAG);
+
+ Document copyDocument = XmlHandler.loadXmlString(xml);
+ Node copyNode = XmlHandler.getSubNode(copyDocument, TransformMeta.XML_TAG);
+ MonetDbBulkLoaderMeta copy = new MonetDbBulkLoaderMeta();
+ copy.loadXml(copyNode, metadataProvider);
+
+ assertEquals(meta.getXml(), copy.getXml());
+
+ assertEquals("unit-test-db", meta.getDbConnectionName());
+ assertEquals("myschema", meta.getSchemaName());
+ assertEquals("mytable", meta.getTableName());
+ assertEquals("100000", meta.getBufferSize());
+ assertEquals("/tmp/monetdb.log", meta.getLogFile());
+ assertTrue(meta.isTruncate());
+ assertTrue(meta.isFullyQuoteSQL());
+ assertEquals("|", meta.getFieldSeparator());
+ assertEquals("\"", meta.getFieldEnclosure());
+ assertEquals("null", meta.getNullRepresentation());
+ assertEquals("UTF-8", meta.getEncoding());
+
+ // Assert field mappings list loaded correctly from XML
+ assertNotNull(meta.getFields());
+ assertEquals(2, meta.getFields().size(), "Expected 2 mapping elements");
+
+ MonetDbBulkLoaderMeta.MonetDbField first = meta.getFields().get(0);
+ assertEquals("col1", first.getFieldTable(), "first mapping stream_name ->
fieldTable");
+ assertEquals("field1", first.getFieldStream(), "first mapping field_name
-> fieldStream");
+ assertTrue(first.isFieldFormatOk(), "first mapping field_format_ok Y ->
true");
+
+ MonetDbBulkLoaderMeta.MonetDbField second = meta.getFields().get(1);
+ assertEquals("col2", second.getFieldTable(), "second mapping stream_name
-> fieldTable");
+ assertEquals("field2", second.getFieldStream(), "second mapping field_name
-> fieldStream");
+ assertFalse(second.isFieldFormatOk(), "second mapping field_format_ok N ->
false");
+
+ // Round-trip: copy should have same field mappings
+ assertNotNull(copy.getFields());
+ assertEquals(meta.getFields().size(), copy.getFields().size());
+ for (int i = 0; i < meta.getFields().size(); i++) {
+ MonetDbBulkLoaderMeta.MonetDbField orig = meta.getFields().get(i);
+ MonetDbBulkLoaderMeta.MonetDbField copied = copy.getFields().get(i);
+ assertEquals(orig.getFieldTable(), copied.getFieldTable(), "field " + i
+ " table");
+ assertEquals(orig.getFieldStream(), copied.getFieldStream(), "field " +
i + " stream");
+ assertEquals(orig.isFieldFormatOk(), copied.isFieldFormatOk(), "field "
+ i + " formatOk");
+ }
+ }
+
+ @Test
+ void testSetDefault() {
+ MonetDbBulkLoaderMeta meta = new MonetDbBulkLoaderMeta();
+ meta.setDefault();
+
+ assertEquals("", meta.getSchemaName());
+ assertNotNull(meta.getTableName());
+ assertEquals("100000", meta.getBufferSize());
+ assertEquals("", meta.getLogFile());
+ assertTrue(meta.isFullyQuoteSQL());
+ assertEquals("|", meta.getFieldSeparator());
+ assertEquals("\"", meta.getFieldEnclosure());
+ assertEquals("", meta.getNullRepresentation());
+ assertEquals("UTF-8", meta.getEncoding());
+ }
+
+ @Test
+ void testClone() {
+ MonetDbBulkLoaderMeta meta = new MonetDbBulkLoaderMeta();
+ meta.setSchemaName("s");
+ meta.setTableName("t");
+
+ MonetDbBulkLoaderMeta clone = (MonetDbBulkLoaderMeta) meta.clone();
+
+ assertNotNull(clone);
+ assertEquals(meta.getSchemaName(), clone.getSchemaName());
+ assertEquals(meta.getTableName(), clone.getTableName());
+ }
+
+ @Test
+ void testGetFieldsDoesNotModifyInputRowMeta() throws Exception {
+ MonetDbBulkLoaderMeta meta = new MonetDbBulkLoaderMeta();
+ IRowMeta inputRowMeta = new RowMeta();
+ int originalSize = inputRowMeta.size();
+
+ meta.getFields(
+ inputRowMeta, "origin", null, null, new Variables(),
(IHopMetadataProvider) null);
+
+ assertEquals(originalSize, inputRowMeta.size());
+ }
+}
diff --git
a/plugins/transforms/monetdbbulkloader/src/test/java/org/apache/hop/pipeline/transforms/monetdbbulkloader/MonetDbBulkLoaderTest.java
b/plugins/transforms/monetdbbulkloader/src/test/java/org/apache/hop/pipeline/transforms/monetdbbulkloader/MonetDbBulkLoaderTest.java
new file mode 100644
index 0000000000..a49fb217b6
--- /dev/null
+++
b/plugins/transforms/monetdbbulkloader/src/test/java/org/apache/hop/pipeline/transforms/monetdbbulkloader/MonetDbBulkLoaderTest.java
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.hop.pipeline.transforms.monetdbbulkloader;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+
+import org.apache.hop.core.HopEnvironment;
+import org.apache.hop.core.plugins.PluginRegistry;
+import org.apache.hop.pipeline.Pipeline;
+import org.apache.hop.pipeline.PipelineMeta;
+import org.apache.hop.pipeline.engines.local.LocalPipelineEngine;
+import org.apache.hop.pipeline.transform.TransformMeta;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+/** Test for MonetDbBulkLoader (excluding dialog). */
+class MonetDbBulkLoaderTest {
+
+ @BeforeEach
+ void setUp() throws Exception {
+ HopEnvironment.init();
+ PluginRegistry.init();
+ }
+
+ @Test
+ void testInitFailsWhenNoConnection() {
+ PipelineMeta pipelineMeta = new PipelineMeta();
+ TransformMeta transformMeta = new TransformMeta("test", new
MonetDbBulkLoaderMeta());
+ pipelineMeta.addTransform(transformMeta);
+
+ MonetDbBulkLoaderMeta meta = new MonetDbBulkLoaderMeta();
+ MonetDbBulkLoaderData data = new MonetDbBulkLoaderData();
+ Pipeline pipeline = new LocalPipelineEngine(pipelineMeta);
+
+ MonetDbBulkLoader transform =
+ new MonetDbBulkLoader(transformMeta, meta, data, 0, pipelineMeta,
pipeline);
+
+ assertFalse(transform.init());
+ }
+
+ @Test
+ void testEscapeOsPathSpacesOnUnix() {
+ PipelineMeta pipelineMeta = new PipelineMeta();
+ TransformMeta transformMeta = new TransformMeta("test", new
MonetDbBulkLoaderMeta());
+ pipelineMeta.addTransform(transformMeta);
+
+ MonetDbBulkLoaderMeta meta = new MonetDbBulkLoaderMeta();
+ MonetDbBulkLoaderData data = new MonetDbBulkLoaderData();
+ Pipeline pipeline = new LocalPipelineEngine(pipelineMeta);
+
+ MonetDbBulkLoader transform =
+ new MonetDbBulkLoader(transformMeta, meta, data, 0, pipelineMeta,
pipeline);
+
+ String result = transform.escapeOsPath("/path with spaces/file", false);
+ assertEquals("/path\\ with\\ spaces/file", result);
+ }
+
+ @Test
+ void testEscapeOsPathSpacesOnWindows() {
+ PipelineMeta pipelineMeta = new PipelineMeta();
+ TransformMeta transformMeta = new TransformMeta("test", new
MonetDbBulkLoaderMeta());
+ pipelineMeta.addTransform(transformMeta);
+
+ MonetDbBulkLoaderMeta meta = new MonetDbBulkLoaderMeta();
+ MonetDbBulkLoaderData data = new MonetDbBulkLoaderData();
+ Pipeline pipeline = new LocalPipelineEngine(pipelineMeta);
+
+ MonetDbBulkLoader transform =
+ new MonetDbBulkLoader(transformMeta, meta, data, 0, pipelineMeta,
pipeline);
+
+ String result = transform.escapeOsPath("C:\\path with spaces\\file", true);
+ assertEquals("C:\\path^ with^ spaces\\file", result);
+ }
+
+ @Test
+ void testEscapeOsPathNoSpaces() {
+ PipelineMeta pipelineMeta = new PipelineMeta();
+ TransformMeta transformMeta = new TransformMeta("test", new
MonetDbBulkLoaderMeta());
+ pipelineMeta.addTransform(transformMeta);
+
+ MonetDbBulkLoaderMeta meta = new MonetDbBulkLoaderMeta();
+ MonetDbBulkLoaderData data = new MonetDbBulkLoaderData();
+ Pipeline pipeline = new LocalPipelineEngine(pipelineMeta);
+
+ MonetDbBulkLoader transform =
+ new MonetDbBulkLoader(transformMeta, meta, data, 0, pipelineMeta,
pipeline);
+
+ String result = transform.escapeOsPath("/path/nospaces", false);
+ assertEquals("/path/nospaces", result);
+ }
+}
diff --git
a/plugins/transforms/monetdbbulkloader/src/test/java/org/apache/hop/pipeline/transforms/monetdbbulkloader/MonetDbVersionTest.java
b/plugins/transforms/monetdbbulkloader/src/test/java/org/apache/hop/pipeline/transforms/monetdbbulkloader/MonetDbVersionTest.java
new file mode 100644
index 0000000000..b112b58a71
--- /dev/null
+++
b/plugins/transforms/monetdbbulkloader/src/test/java/org/apache/hop/pipeline/transforms/monetdbbulkloader/MonetDbVersionTest.java
@@ -0,0 +1,108 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.hop.pipeline.transforms.monetdbbulkloader;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+
+class MonetDbVersionTest {
+
+ @Test
+ void testConstructorWithInts() {
+ MonetDbVersion v = new MonetDbVersion(11, 17, 17);
+ assertEquals(11, v.getMajorVersion());
+ assertEquals(17, v.getMinorVersion());
+ assertEquals(17, v.getPatchVersion());
+ }
+
+ @Test
+ void testParseFullVersion() throws MonetDbVersionException {
+ MonetDbVersion v = new MonetDbVersion("11.17.17");
+ assertEquals(11, v.getMajorVersion());
+ assertEquals(17, v.getMinorVersion());
+ assertEquals(17, v.getPatchVersion());
+ }
+
+ @Test
+ void testParseMajorMinorOnly() throws MonetDbVersionException {
+ MonetDbVersion v = new MonetDbVersion("11.0");
+ assertEquals(11, v.getMajorVersion());
+ assertEquals(0, v.getMinorVersion());
+ assertNull(v.getPatchVersion());
+ }
+
+ @Test
+ void testParseWithExtraParts() throws MonetDbVersionException {
+ MonetDbVersion v = new MonetDbVersion("11.5.17.1");
+ assertEquals(11, v.getMajorVersion());
+ assertEquals(5, v.getMinorVersion());
+ assertEquals(17, v.getPatchVersion());
+ }
+
+ @Test
+ void testParseThrowsWhenNull() {
+ MonetDbVersionException e =
+ assertThrows(MonetDbVersionException.class, () -> new
MonetDbVersion(null));
+ assertTrue(e.getMessage() != null && e.getMessage().contains("null"));
+ }
+
+ @Test
+ void testParseThrowsWhenInvalidFormat() {
+ assertThrows(MonetDbVersionException.class, () -> new MonetDbVersion(""));
+ assertThrows(MonetDbVersionException.class, () -> new
MonetDbVersion("abc"));
+ assertThrows(MonetDbVersionException.class, () -> new
MonetDbVersion("11.17.a"));
+ assertThrows(MonetDbVersionException.class, () -> new
MonetDbVersion("11-17-17"));
+ }
+
+ @Test
+ void testCompareTo() throws MonetDbVersionException {
+ MonetDbVersion v111717 = new MonetDbVersion("11.17.17");
+ MonetDbVersion v111717b = new MonetDbVersion("11.17.17");
+ MonetDbVersion v111800 = new MonetDbVersion("11.18.0");
+ MonetDbVersion v120000 = new MonetDbVersion("12.0.0");
+
+ assertEquals(0, v111717.compareTo(v111717b));
+ assertTrue(v111717.compareTo(v111800) < 0);
+ assertTrue(v111800.compareTo(v111717) > 0);
+ assertTrue(v111717.compareTo(v120000) < 0);
+ assertTrue(v120000.compareTo(v111717) > 0);
+ }
+
+ @Test
+ void testCompareToJan2014Sp2() throws MonetDbVersionException {
+ MonetDbVersion older = new MonetDbVersion("11.17.16");
+ MonetDbVersion same = new MonetDbVersion("11.17.17");
+ MonetDbVersion newer = new MonetDbVersion("11.17.18");
+
+ assertTrue(older.compareTo(MonetDbVersion.JAN_2014_SP2_DB_VERSION) < 0);
+ assertEquals(0, same.compareTo(MonetDbVersion.JAN_2014_SP2_DB_VERSION));
+ assertTrue(newer.compareTo(MonetDbVersion.JAN_2014_SP2_DB_VERSION) > 0);
+ }
+
+ @Test
+ void testToString() throws MonetDbVersionException {
+ MonetDbVersion v = new MonetDbVersion("11.17.17");
+ String s = v.toString();
+ assertTrue(s.contains("11"));
+ assertTrue(s.contains("17"));
+ }
+}
diff --git
a/plugins/transforms/monetdbbulkloader/src/test/resources/monetdbbulkloader-transform.xml
b/plugins/transforms/monetdbbulkloader/src/test/resources/monetdbbulkloader-transform.xml
new file mode 100644
index 0000000000..3ca8117e2a
--- /dev/null
+++
b/plugins/transforms/monetdbbulkloader/src/test/resources/monetdbbulkloader-transform.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Licensed to the Apache Software Foundation (ASF) under one or more
+ ~ contributor license agreements. See the NOTICE file distributed with
+ ~ this work for additional information regarding copyright ownership.
+ ~ The ASF licenses this file to You 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.
+ ~
+ -->
+
+<transform>
+ <name>MonetDB Bulk Loader</name>
+ <type>MonetDBBulkLoader</type>
+ <description/>
+ <distribute>Y</distribute>
+ <custom_distribution/>
+ <copies>1</copies>
+ <partitioning>
+ <method>none</method>
+ <schema_name/>
+ </partitioning>
+ <connection>unit-test-db</connection>
+ <buffer_size>100000</buffer_size>
+ <schema>myschema</schema>
+ <table>mytable</table>
+ <log_file>/tmp/monetdb.log</log_file>
+ <truncate>Y</truncate>
+ <fully_quote_sql>Y</fully_quote_sql>
+ <field_separator>|</field_separator>
+ <field_enclosure>"</field_enclosure>
+ <null_representation>null</null_representation>
+ <encoding>UTF-8</encoding>
+ <mapping>
+ <stream_name>col1</stream_name>
+ <field_name>field1</field_name>
+ <field_format_ok>Y</field_format_ok>
+ </mapping>
+ <mapping>
+ <stream_name>col2</stream_name>
+ <field_name>field2</field_name>
+ <field_format_ok>N</field_format_ok>
+ </mapping>
+ <attributes/>
+ <GUI>
+ <xloc>368</xloc>
+ <yloc>96</yloc>
+ </GUI>
+</transform>