This is an automated email from the ASF dual-hosted git repository.
mattcasters 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 446bb7ea04 Fix AI-reported bugs against current main type rules (#8244)
446bb7ea04 is described below
commit 446bb7ea04b6b40ef09cba21637f37dfaa4a9c1a
Author: Matt Casters <[email protected]>
AuthorDate: Wed Sep 9 21:26:41 2026 +0200
Fix AI-reported bugs against current main type rules (#8244)
* Fix AI-reported bugs against current main type rules
Verified against 2.20 DatabaseTypeMapper rather than 2.19
getFieldDefinition.
- #8216: store JDBC precision as Hop Number length; keep numericLength() as
integer digits for dialect rules; PostgreSQL writes NUMERIC(length, scale)
- #8218: timestamp parse mask reads HOP_DEFAULT_TIMESTAMP_FORMAT
- #8219: Stream Lookup handles Timestamp default values
- #8220/#8221: SQL Server DATE/TIME/DATETIME2 and NVARCHAR/VARCHAR(MAX) via
type rules
- #8222: parameter metadata uses StandardJdbcTypeMapper
- #8223: skip JDBC getColumns() for ordinary databases
- #8225: do not list empty unknown metadata folders
- #8226: split catalog.schema even when supportsCatalogs() is false
- #8227: ProgressMonitorDialog closes when the runnable returns
- #8230: remove dead terminal menu constants
Fixes #8216, #8218, #8219, #8220, #8221, #8222, #8223, #8225, #8226, #8227,
#8230
* Address review on Get Fields errors, PostgreSQL NUMERIC, and query
fallback
- Re-throw progress-dialog exceptions after the shell is disposed, and drop
the extra monitor.done() so a failed Get Fields still shows the error
dialog
- Clamp PostgreSQL NUMERIC precision to at least the scale (PG < 15 rejects
s > p); document the 2.20 NUMERIC(length, scale) meaning
- Do not run getQueryFieldsFallback twice when that path already failed
- Prefer Hop Timestamp over original JDBC DATE when generating SQL Server
DDL
- Map unsized NUMERIC/DECIMAL parameters back to Integer
---
.../org/apache/hop/core/database/Database.java | 160 +++++++++------------
.../org/apache/hop/core/database/DatabaseMeta.java | 16 ++-
.../database/types/StandardJdbcTypeMapper.java | 25 +++-
.../apache/hop/core/row/value/ValueMetaBase.java | 2 +-
.../apache/hop/core/database/DatabaseMetaTest.java | 14 ++
.../org/apache/hop/core/database/DatabaseTest.java | 41 ++++++
.../hop/core/row/value/ValueMetaTimestampTest.java | 7 +
.../JdbcTypeMappingCharacterizationTest.txt | 96 ++++++-------
.../modules/ROOT/pages/database/column-types.adoc | 15 +-
.../ROOT/pages/database/databases/postgresql.adoc | 8 ++
.../CockroachFieldDefinitionGoldenTest.txt | 12 +-
.../GreenplumFieldDefinitionGoldenTest.txt | 12 +-
.../databases/mssql/MsSqlServerDatabaseMeta.java | 87 +++++++++--
.../mssql/MsSqlServerDatabaseMetaTest.java | 4 +-
...sSqlServerDatabaseMeta_FieldDefinitionTest.java | 3 +-
.../databases/mssql/MsSqlServerTypeRulesTest.java | 138 ++++++++++++++++++
.../mssql/MsSqlServerFieldDefinitionGoldenTest.txt | 88 ++++++------
.../MsSqlServerNativeDatabaseMetaTest.java | 24 ++++
.../MsSqlServerNativeFieldDefinitionGoldenTest.txt | 88 ++++++------
.../hop/databases/mysql/MySqlTypeRulesTest.java | 18 +++
.../postgresql/PostgreSqlDatabaseMeta.java | 6 +-
.../postgresql/PostgreSqlDatabaseMetaTest.java | 10 +-
.../postgresql/PostgreSqlTypeRulesTest.java | 18 ++-
.../PostgreSqlFieldDefinitionGoldenTest.txt | 12 +-
.../redshift/RedshiftFieldDefinitionGoldenTest.txt | 12 +-
.../transforms/streamlookup/StreamLookup.java | 2 +-
.../transforms/streamlookup/StreamLookupTest.java | 81 +++++++++++
.../main/java/org/apache/hop/ui/core/PropsUi.java | 1 -
.../hop/ui/core/dialog/ProgressMonitorDialog.java | 11 ++
.../main/java/org/apache/hop/ui/hopgui/HopGui.java | 2 -
.../perspective/metadata/MetadataPerspective.java | 6 +-
31 files changed, 724 insertions(+), 295 deletions(-)
diff --git a/core/src/main/java/org/apache/hop/core/database/Database.java
b/core/src/main/java/org/apache/hop/core/database/Database.java
index a66a658906..bc44cf0e33 100644
--- a/core/src/main/java/org/apache/hop/core/database/Database.java
+++ b/core/src/main/java/org/apache/hop/core/database/Database.java
@@ -2287,33 +2287,25 @@ public class Database implements IVariables,
ILoggingObject, AutoCloseable {
// For now, we just try to get the field layout on the re-bound in the
// exception block below.
//
- boolean maybeScanTable = false;
try {
if (databaseMeta.supportsPreparedStatementMetadataRetrieval()) {
- // On with the regular program.
- //
fields = getQueryFieldsFromPreparedStatement(sql);
+ } else if (isDataServiceConnection()) {
+ // Data services return Hop type names on SOURCE_* columns. Standard
JDBC getColumns()
+ // does not, so ordinary databases go straight to the fallback.
+ fields = getQueryFieldsFromDatabaseMetaData(sql);
} else {
- if (isDataServiceConnection()) {
- fields = getQueryFieldsFromDatabaseMetaData(sql);
- } else {
- maybeScanTable = true;
- fields = getQueryFieldsFromDatabaseMetaData();
- }
+ fields = getQueryFieldsFallback(sql, param, inform, data);
}
} catch (Exception e) {
- if (maybeScanTable) {
- String fastFetchSql = sql.replaceAll("\\b((?i)WHERE)\\b(\\s)", "$1 1=2
AND$2");
- if (fastFetchSql.length() > sql.length()) {
- try {
- fields = getQueryFieldsFallback(sql, param, inform, data);
- } catch (HopDatabaseException ignore) {
- // Do nothing
- }
- }
- }
- if (fields == null) {
+ // Only recover from the prepared-statement / data-service paths. The
else branch already
+ // ran the fallback; calling it again would execute the user's SQL twice.
+ if (databaseMeta.supportsPreparedStatementMetadataRetrieval() ||
isDataServiceConnection()) {
fields = getQueryFieldsFallback(sql, param, inform, data);
+ } else if (e instanceof HopDatabaseException hopDatabaseException) {
+ throw hopDatabaseException;
+ } else {
+ throw new HopDatabaseException(e);
}
}
@@ -2358,54 +2350,55 @@ public class Database implements IVariables,
ILoggingObject, AutoCloseable {
private IRowMeta getQueryFieldsFromDatabaseMetaData(String sql) throws
Exception {
- ResultSet columns =
+ try (ResultSet columns =
connection
.getMetaData()
- .getColumns("", "", StringUtils.isNotBlank(sql) ? sql :
databaseMeta.getName(), "");
- IRowMeta rowMeta = new RowMeta();
- while (columns.next()) {
- IValueMeta valueMeta = null;
- String name = columns.getString("COLUMN_NAME");
- String comments = columns.getString("REMARKS");
- String type = columns.getString("SOURCE_DATA_TYPE");
- int size = columns.getInt("COLUMN_SIZE");
- valueMeta =
- switch (type) {
- case "Integer", "Long" -> new ValueMetaInteger();
- case "BigDecimal", "BigNumber" -> new ValueMetaBigNumber();
- case "Double", "Number" -> new ValueMetaNumber();
- case "String" -> new ValueMetaString();
- case "Date" -> new ValueMetaDate();
- case "Boolean" -> new ValueMetaBoolean();
- case "Binary" -> new ValueMetaBinary();
- case "Timestamp" -> new ValueMetaTimestamp();
- case "Internet Address" -> new ValueMetaInternetAddress();
- default -> valueMeta;
- };
- if (valueMeta != null) {
- valueMeta.setName(name);
- valueMeta.setComments(comments);
- valueMeta.setLength(size);
- valueMeta.setOriginalColumnTypeName(type);
-
- valueMeta.setConversionMask(columns.getString("SOURCE_MASK"));
- valueMeta.setDecimalSymbol(columns.getString("SOURCE_DECIMAL_SYMBOL"));
-
valueMeta.setGroupingSymbol(columns.getString("SOURCE_GROUPING_SYMBOL"));
-
valueMeta.setCurrencySymbol(columns.getString("SOURCE_CURRENCY_SYMBOL"));
-
- rowMeta.addValueMeta(valueMeta);
+ .getColumns("", "", StringUtils.isNotBlank(sql) ? sql :
databaseMeta.getName(), "")) {
+ IRowMeta rowMeta = new RowMeta();
+ while (columns.next()) {
+ IValueMeta valueMeta = null;
+ String name = columns.getString("COLUMN_NAME");
+ String comments = columns.getString("REMARKS");
+ String type = columns.getString("SOURCE_DATA_TYPE");
+ int size = columns.getInt("COLUMN_SIZE");
+ valueMeta =
+ switch (type) {
+ case "Integer", "Long" -> new ValueMetaInteger();
+ case "BigDecimal", "BigNumber" -> new ValueMetaBigNumber();
+ case "Double", "Number" -> new ValueMetaNumber();
+ case "String" -> new ValueMetaString();
+ case "Date" -> new ValueMetaDate();
+ case "Boolean" -> new ValueMetaBoolean();
+ case "Binary" -> new ValueMetaBinary();
+ case "Timestamp" -> new ValueMetaTimestamp();
+ case "Internet Address" -> new ValueMetaInternetAddress();
+ default -> valueMeta;
+ };
+ if (valueMeta != null) {
+ valueMeta.setName(name);
+ valueMeta.setComments(comments);
+ valueMeta.setLength(size);
+ valueMeta.setOriginalColumnTypeName(type);
+
+ valueMeta.setConversionMask(columns.getString("SOURCE_MASK"));
+
valueMeta.setDecimalSymbol(columns.getString("SOURCE_DECIMAL_SYMBOL"));
+
valueMeta.setGroupingSymbol(columns.getString("SOURCE_GROUPING_SYMBOL"));
+
valueMeta.setCurrencySymbol(columns.getString("SOURCE_CURRENCY_SYMBOL"));
+
+ rowMeta.addValueMeta(valueMeta);
+ } else {
+ log.logBasic(
+ "Database.getQueryFields() IValueMeta mapping not resolved for
the column " + name);
+ rowMeta = null;
+ break;
+ }
+ }
+ if (rowMeta != null && !rowMeta.isEmpty()) {
+ return rowMeta;
} else {
- log.logBasic(
- "Database.getQueryFields() IValueMeta mapping not resolved for the
column " + name);
- rowMeta = null;
- break;
+ throw new Exception("Error in Database.getQueryFields()");
}
}
- if (rowMeta != null && !rowMeta.isEmpty()) {
- return rowMeta;
- } else {
- throw new Exception("Error in Database.getQueryFields()");
- }
}
public IRowMeta getQueryFieldsFallback(String sql, boolean param, IRowMeta
inform, Object[] data)
@@ -3563,33 +3556,18 @@ public class Database implements IVariables,
ILoggingObject, AutoCloseable {
ParameterMetaData pmd = ps.getParameterMetaData();
for (int i = 1; i <= pmd.getParameterCount(); i++) {
String name = "par" + i;
- int sqltype = pmd.getParameterType(i);
- int length = pmd.getPrecision(i);
- int precision = pmd.getScale(i);
- IValueMeta val =
- switch (sqltype) {
- case java.sql.Types.CHAR, java.sql.Types.VARCHAR -> new
ValueMetaString(name);
- case java.sql.Types.BIGINT,
- java.sql.Types.INTEGER,
- java.sql.Types.NUMERIC,
- java.sql.Types.SMALLINT,
- java.sql.Types.TINYINT ->
- new ValueMetaInteger(name);
- case java.sql.Types.DECIMAL,
- java.sql.Types.DOUBLE,
- java.sql.Types.FLOAT,
- java.sql.Types.REAL ->
- new ValueMetaNumber(name);
- case java.sql.Types.DATE, java.sql.Types.TIME,
java.sql.Types.TIMESTAMP ->
- new ValueMetaDate(name);
- case java.sql.Types.BOOLEAN, java.sql.Types.BIT -> new
ValueMetaBoolean(name);
- default -> new ValueMetaNone(name);
- };
-
- if (val.isNumeric() && (length > 18 || precision > 18)) {
- val = new ValueMetaBigNumber(name);
+ int sqlType = pmd.getParameterType(i);
+ int precision = pmd.getPrecision(i);
+ int scale = pmd.getScale(i);
+ DatabaseColumn column = DatabaseColumn.of(name, sqlType, null,
precision, scale, precision);
+ IValueMeta val = DatabaseTypeMapper.getValueMeta(this, databaseMeta,
column, false, false);
+ if (val == null) {
+ val = new ValueMetaNone(name);
+ } else if (isUnsizedExactNumeric(sqlType, precision, scale) &&
val.isNumber()) {
+ // Drivers often report NUMERIC/DECIMAL with precision 0 for untyped
parameters.
+ // The mapper then yields a double-backed Number; the old parameter
path used Integer.
+ val = new ValueMetaInteger(name);
}
-
par.addValueMeta(val);
}
} catch (AbstractMethodError | Exception e) {
@@ -3600,6 +3578,10 @@ public class Database implements IVariables,
ILoggingObject, AutoCloseable {
return par;
}
+ private static boolean isUnsizedExactNumeric(int sqlType, int precision, int
scale) {
+ return (sqlType == Types.NUMERIC || sqlType == Types.DECIMAL) && precision
<= 0 && scale <= 0;
+ }
+
public int countParameters(String sql) {
int q = 0;
boolean quoteOpened = false;
diff --git a/core/src/main/java/org/apache/hop/core/database/DatabaseMeta.java
b/core/src/main/java/org/apache/hop/core/database/DatabaseMeta.java
index 4fc82f3918..2e7bf5d47a 100644
--- a/core/src/main/java/org/apache/hop/core/database/DatabaseMeta.java
+++ b/core/src/main/java/org/apache/hop/core/database/DatabaseMeta.java
@@ -1097,13 +1097,15 @@ public class DatabaseMeta extends HopMetadataBase
implements Cloneable, IHopMeta
}
private String quoteSchema(String schemaName) {
- if (supportsCatalogs()) {
- int separatorIndex = schemaName.indexOf('.');
- if (separatorIndex > 0 && separatorIndex < schemaName.length() - 1) {
- String catalogName = schemaName.substring(0, separatorIndex);
- String schemaPart = schemaName.substring(separatorIndex + 1);
- return quoteField(catalogName) + "." + quoteField(schemaPart);
- }
+ // A composite "catalog.schema" is split whenever the caller passed one,
including on dialects
+ // that return supportsCatalogs() == false (jTDS SQL Server, Access,
Gupta, Iris). That flag is
+ // a browsing hint and must not collapse mydb.dbo into the unresolvable
identifier [mydb.dbo].
+ // A schema whose name itself contains a literal dot is vanishingly rare
next to catalog.schema.
+ int separatorIndex = schemaName.indexOf('.');
+ if (separatorIndex > 0 && separatorIndex < schemaName.length() - 1) {
+ String catalogName = schemaName.substring(0, separatorIndex);
+ String schemaPart = schemaName.substring(separatorIndex + 1);
+ return quoteField(catalogName) + "." + quoteField(schemaPart);
}
return quoteField(schemaName);
}
diff --git
a/core/src/main/java/org/apache/hop/core/database/types/StandardJdbcTypeMapper.java
b/core/src/main/java/org/apache/hop/core/database/types/StandardJdbcTypeMapper.java
index 9548e70033..751c809cbc 100644
---
a/core/src/main/java/org/apache/hop/core/database/types/StandardJdbcTypeMapper.java
+++
b/core/src/main/java/org/apache/hop/core/database/types/StandardJdbcTypeMapper.java
@@ -197,7 +197,9 @@ public final class StandardJdbcTypeMapper {
*/
private static Numeric mapDecimal(DatabaseMeta databaseMeta, DatabaseColumn
column, int sqlType) {
int valtype = IValueMeta.TYPE_NUMBER;
- int length = numericLength(column);
+ // Hop Number/BigNumber length is the total number of significant digits
(JDBC precision).
+ // numericLength() remains the digits before the decimal, for dialect rule
conditions.
+ int length = totalDigits(column);
int precision = numericScale(column);
if (isApproximateNumeric(sqlType)) {
@@ -271,10 +273,13 @@ public final class StandardJdbcTypeMapper {
}
/**
- * The Hop length of a numeric column: the digits before the decimal.
+ * Digits before the decimal: JDBC precision with the scale taken off.
*
- * <p>A database reports precision as the total number of significant
digits, so the scale has to
- * come off. A value at or beyond 126 means the database did not really say.
+ * <p>Dialect rules use this to test whether a declaration is possible at
all (an overscaled
+ * DOUBLE, an unspecified NUMERIC). It is not the Hop length stored on the
value metadata; that is
+ * {@link #totalDigits(DatabaseColumn)}.
+ *
+ * <p>A value at or beyond 126 means the database did not really say.
*/
public static int numericLength(DatabaseColumn column) {
int length = column.getPrecision();
@@ -285,6 +290,18 @@ public final class StandardJdbcTypeMapper {
return length >= 126 ? -1 : length;
}
+ /**
+ * The Hop length of a numeric column: the total number of significant
digits, the same value the
+ * database reports as its precision. Dialogs and {@code DECIMAL(length,
precision)} writers use
+ * this meaning.
+ *
+ * <p>A value at or beyond 126 means the database did not really say.
+ */
+ public static int totalDigits(DatabaseColumn column) {
+ int length = column.getPrecision();
+ return length >= 126 ? -1 : length;
+ }
+
/** The Hop precision of a numeric column: the digits after the decimal. */
public static int numericScale(DatabaseColumn column) {
int scale = column.getScale();
diff --git
a/core/src/main/java/org/apache/hop/core/row/value/ValueMetaBase.java
b/core/src/main/java/org/apache/hop/core/row/value/ValueMetaBase.java
index 4bf25f1d03..652a9d0583 100644
--- a/core/src/main/java/org/apache/hop/core/row/value/ValueMetaBase.java
+++ b/core/src/main/java/org/apache/hop/core/row/value/ValueMetaBase.java
@@ -125,7 +125,7 @@ public class ValueMetaBase implements IValueMeta {
EnvUtil.getSystemProperty(Const.HOP_DEFAULT_DATE_FORMAT),
"yyyy/MM/dd HH:mm:ss.SSS");
public static final String DEFAULT_TIMESTAMP_PARSE_MASK =
Const.NVL(
- EnvUtil.getSystemProperty(Const.HOP_DEFAULT_DATE_FORMAT),
+ EnvUtil.getSystemProperty(Const.HOP_DEFAULT_TIMESTAMP_FORMAT),
"yyyy/MM/dd HH:mm:ss.SSSSSSSSS");
// endregion
diff --git
a/core/src/test/java/org/apache/hop/core/database/DatabaseMetaTest.java
b/core/src/test/java/org/apache/hop/core/database/DatabaseMetaTest.java
index efc45966e2..0aa49e2ba9 100644
--- a/core/src/test/java/org/apache/hop/core/database/DatabaseMetaTest.java
+++ b/core/src/test/java/org/apache/hop/core/database/DatabaseMetaTest.java
@@ -196,6 +196,20 @@ class DatabaseMetaTest {
databaseMeta.getQuotedSchemaTableCombination(variables,
"catalog.schema", "sample"));
}
+ @Test
+ void splitsCompositeSchemaEvenWhenCatalogsAreUnsupported() {
+ when(iDatabase.getStartQuote()).thenReturn("[");
+ when(iDatabase.getEndQuote()).thenReturn("]");
+ when(iDatabase.isSupportsCatalogs()).thenReturn(false);
+ when(iDatabase.isQuoteAllFields()).thenReturn(true);
+ when(iDatabase.getSchemaTableCombination(anyString(), anyString()))
+ .thenAnswer(invocation -> invocation.getArgument(0) + "." +
invocation.getArgument(1));
+
+ assertEquals(
+ "[catalog].[schema].[sample]",
+ databaseMeta.getQuotedSchemaTableCombination(variables,
"catalog.schema", "sample"));
+ }
+
@Test
@SuppressWarnings("unchecked")
void testModifyingName() {
diff --git a/core/src/test/java/org/apache/hop/core/database/DatabaseTest.java
b/core/src/test/java/org/apache/hop/core/database/DatabaseTest.java
index 5f2dd67a60..3ec4b3d3ce 100644
--- a/core/src/test/java/org/apache/hop/core/database/DatabaseTest.java
+++ b/core/src/test/java/org/apache/hop/core/database/DatabaseTest.java
@@ -43,6 +43,7 @@ import java.lang.reflect.Field;
import java.sql.BatchUpdateException;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
+import java.sql.ParameterMetaData;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
@@ -140,6 +141,46 @@ class DatabaseTest {
assertEquals(columnSize, iRowMeta.getValueMeta(0).getLength());
}
+ @Test
+ void getParameterMetaDataMapsNvarcharAndNumeric() throws Exception {
+ when(meta.getIDatabase()).thenReturn(new NoneDatabaseMeta());
+ ParameterMetaData parameterMetaData = mock(ParameterMetaData.class);
+ when(ps.getParameterMetaData()).thenReturn(parameterMetaData);
+ when(parameterMetaData.getParameterCount()).thenReturn(2);
+ when(parameterMetaData.getParameterType(1)).thenReturn(Types.NVARCHAR);
+ when(parameterMetaData.getPrecision(1)).thenReturn(20);
+ when(parameterMetaData.getScale(1)).thenReturn(0);
+ when(parameterMetaData.getParameterType(2)).thenReturn(Types.NUMERIC);
+ when(parameterMetaData.getPrecision(2)).thenReturn(18);
+ when(parameterMetaData.getScale(2)).thenReturn(4);
+
+ Database db = new Database(log, variables, meta);
+ IRowMeta rowMeta = db.getParameterMetaData(ps);
+
+ assertEquals(2, rowMeta.size());
+ assertTrue(rowMeta.getValueMeta(0).isString());
+ assertTrue(rowMeta.getValueMeta(1).isNumeric());
+ assertFalse(rowMeta.getValueMeta(1).isInteger());
+ assertEquals(4, rowMeta.getValueMeta(1).getPrecision());
+ }
+
+ @Test
+ void getParameterMetaDataMapsUnsizedNumericToInteger() throws Exception {
+ when(meta.getIDatabase()).thenReturn(new NoneDatabaseMeta());
+ ParameterMetaData parameterMetaData = mock(ParameterMetaData.class);
+ when(ps.getParameterMetaData()).thenReturn(parameterMetaData);
+ when(parameterMetaData.getParameterCount()).thenReturn(1);
+ when(parameterMetaData.getParameterType(1)).thenReturn(Types.NUMERIC);
+ when(parameterMetaData.getPrecision(1)).thenReturn(0);
+ when(parameterMetaData.getScale(1)).thenReturn(0);
+
+ Database db = new Database(log, variables, meta);
+ IRowMeta rowMeta = db.getParameterMetaData(ps);
+
+ assertEquals(1, rowMeta.size());
+ assertTrue(rowMeta.getValueMeta(0).isInteger());
+ }
+
/**
* When using getLookup calls there is no need to make attempt to retrieve
row set metadata for
* every call. That may bring performance penalty depends on jdbc driver
implementation. For some
diff --git
a/core/src/test/java/org/apache/hop/core/row/value/ValueMetaTimestampTest.java
b/core/src/test/java/org/apache/hop/core/row/value/ValueMetaTimestampTest.java
index 9774ce683d..3513023f2f 100644
---
a/core/src/test/java/org/apache/hop/core/row/value/ValueMetaTimestampTest.java
+++
b/core/src/test/java/org/apache/hop/core/row/value/ValueMetaTimestampTest.java
@@ -39,6 +39,13 @@ import org.mockito.stubbing.Answer;
@ExtendWith(RestoreHopEnvironmentExtension.class)
class ValueMetaTimestampTest {
+ @Test
+ void timestampParseMaskUsesTheTimestampPattern() {
+ assertEquals("yyyy/MM/dd HH:mm:ss.SSSSSSSSS",
ValueMetaBase.DEFAULT_TIMESTAMP_PARSE_MASK);
+ assertEquals(
+ ValueMetaBase.DEFAULT_TIMESTAMP_FORMAT_MASK,
ValueMetaBase.DEFAULT_TIMESTAMP_PARSE_MASK);
+ }
+
@Test
void testSetPreparedStatementValue() throws Exception {
ValueMetaTimestamp vm = new ValueMetaTimestamp();
diff --git
a/core/src/test/resources/org/apache/hop/core/database/JdbcTypeMappingCharacterizationTest.txt
b/core/src/test/resources/org/apache/hop/core/database/JdbcTypeMappingCharacterizationTest.txt
index 654cb89e3c..ddae6f4f81 100644
---
a/core/src/test/resources/org/apache/hop/core/database/JdbcTypeMappingCharacterizationTest.txt
+++
b/core/src/test/resources/org/apache/hop/core/database/JdbcTypeMappingCharacterizationTest.txt
@@ -59,32 +59,32 @@ TINYINT p= 16 s= 16 signed | DB=Integer(2,0)
VMB=Integer(2,0)
TINYINT p= 126 s= 0 signed | DB=Integer(2,0) VMB=Integer(2,0)
PRV=Integer(2,0) NEW=Integer(2,0)
DECIMAL p= 0 s= 0 signed | DB=Number(0,0) VMB=Number(0,0)
PRV=Number(0,0) NEW=Number(0,0)
DECIMAL p= 38 s= 0 signed | DB=BigNumber(38,0)
VMB=BigNumber(38,0) PRV=BigNumber(38,0) NEW=BigNumber(38,0)
-DECIMAL p= 10 s= 2 signed | DB=Number(8,2) VMB=Number(10,2)
PRV=Number(10,2) NEW=Number(8,2) DIVERGE
-DECIMAL p= 20 s= 10 signed | DB=Number(10,10)
VMB=BigNumber(20,10) PRV=BigNumber(20,10) NEW=Number(10,10) DIVERGE
+DECIMAL p= 10 s= 2 signed | DB=Number(10,2) VMB=Number(10,2)
PRV=Number(10,2) NEW=Number(10,2)
+DECIMAL p= 20 s= 10 signed | DB=BigNumber(20,10)
VMB=BigNumber(20,10) PRV=BigNumber(20,10) NEW=BigNumber(20,10)
DECIMAL p= 16 s= 16 signed | DB=BigNumber(16,16)
VMB=BigNumber(16,16) PRV=BigNumber(16,16) NEW=BigNumber(16,16)
DECIMAL p= 126 s= 0 signed | DB=Number(-1,0) VMB=Number(-1,0)
PRV=Number(-1,0) NEW=Number(-1,0)
NUMERIC p= 0 s= 0 signed | DB=Number(0,0) VMB=Number(0,0)
PRV=Number(0,0) NEW=Number(0,0)
NUMERIC p= 38 s= 0 signed | DB=BigNumber(38,0)
VMB=BigNumber(38,0) PRV=BigNumber(38,0) NEW=BigNumber(38,0)
-NUMERIC p= 10 s= 2 signed | DB=Number(8,2) VMB=Number(10,2)
PRV=Number(10,2) NEW=Number(8,2) DIVERGE
-NUMERIC p= 20 s= 10 signed | DB=Number(10,10)
VMB=BigNumber(20,10) PRV=BigNumber(20,10) NEW=Number(10,10) DIVERGE
+NUMERIC p= 10 s= 2 signed | DB=Number(10,2) VMB=Number(10,2)
PRV=Number(10,2) NEW=Number(10,2)
+NUMERIC p= 20 s= 10 signed | DB=BigNumber(20,10)
VMB=BigNumber(20,10) PRV=BigNumber(20,10) NEW=BigNumber(20,10)
NUMERIC p= 16 s= 16 signed | DB=BigNumber(16,16)
VMB=BigNumber(16,16) PRV=BigNumber(16,16) NEW=BigNumber(16,16)
NUMERIC p= 126 s= 0 signed | DB=Number(-1,0) VMB=Number(-1,0)
PRV=Number(-1,0) NEW=Number(-1,0)
DOUBLE p= 0 s= 0 signed | DB=Number(0,-1) VMB=Number(0,-1)
PRV=Number(0,-1) NEW=Number(0,-1)
DOUBLE p= 38 s= 0 signed | DB=BigNumber(38,-1)
VMB=BigNumber(38,-1) PRV=BigNumber(38,-1) NEW=BigNumber(38,-1)
-DOUBLE p= 10 s= 2 signed | DB=Number(8,2) VMB=Number(10,2)
PRV=Number(10,2) NEW=Number(8,2) DIVERGE
-DOUBLE p= 20 s= 10 signed | DB=Number(10,10)
VMB=BigNumber(20,10) PRV=BigNumber(20,10) NEW=Number(10,10) DIVERGE
+DOUBLE p= 10 s= 2 signed | DB=Number(10,2) VMB=Number(10,2)
PRV=Number(10,2) NEW=Number(10,2)
+DOUBLE p= 20 s= 10 signed | DB=BigNumber(20,10)
VMB=BigNumber(20,10) PRV=BigNumber(20,10) NEW=BigNumber(20,10)
DOUBLE p= 16 s= 16 signed | DB=BigNumber(16,16)
VMB=BigNumber(16,16) PRV=BigNumber(16,16) NEW=BigNumber(16,16)
DOUBLE p= 126 s= 0 signed | DB=Number(-1,-1) VMB=Number(-1,-1)
PRV=Number(-1,-1) NEW=Number(-1,-1)
FLOAT p= 0 s= 0 signed | DB=Number(0,-1) VMB=Number(0,-1)
PRV=Number(0,-1) NEW=Number(0,-1)
FLOAT p= 38 s= 0 signed | DB=BigNumber(38,-1)
VMB=BigNumber(38,-1) PRV=BigNumber(38,-1) NEW=BigNumber(38,-1)
-FLOAT p= 10 s= 2 signed | DB=Number(8,2) VMB=Number(10,2)
PRV=Number(10,2) NEW=Number(8,2) DIVERGE
-FLOAT p= 20 s= 10 signed | DB=Number(10,10)
VMB=BigNumber(20,10) PRV=BigNumber(20,10) NEW=Number(10,10) DIVERGE
+FLOAT p= 10 s= 2 signed | DB=Number(10,2) VMB=Number(10,2)
PRV=Number(10,2) NEW=Number(10,2)
+FLOAT p= 20 s= 10 signed | DB=BigNumber(20,10)
VMB=BigNumber(20,10) PRV=BigNumber(20,10) NEW=BigNumber(20,10)
FLOAT p= 16 s= 16 signed | DB=BigNumber(16,16)
VMB=BigNumber(16,16) PRV=BigNumber(16,16) NEW=BigNumber(16,16)
FLOAT p= 126 s= 0 signed | DB=Number(-1,-1) VMB=Number(-1,-1)
PRV=Number(-1,-1) NEW=Number(-1,-1)
REAL p= 0 s= 0 signed | DB=Number(0,-1) VMB=Number(0,-1)
PRV=Number(0,-1) NEW=Number(0,-1)
REAL p= 38 s= 0 signed | DB=BigNumber(38,-1)
VMB=BigNumber(38,-1) PRV=BigNumber(38,-1) NEW=BigNumber(38,-1)
-REAL p= 10 s= 2 signed | DB=Number(8,2) VMB=Number(10,2)
PRV=Number(10,2) NEW=Number(8,2) DIVERGE
-REAL p= 20 s= 10 signed | DB=Number(10,10)
VMB=BigNumber(20,10) PRV=BigNumber(20,10) NEW=Number(10,10) DIVERGE
+REAL p= 10 s= 2 signed | DB=Number(10,2) VMB=Number(10,2)
PRV=Number(10,2) NEW=Number(10,2)
+REAL p= 20 s= 10 signed | DB=BigNumber(20,10)
VMB=BigNumber(20,10) PRV=BigNumber(20,10) NEW=BigNumber(20,10)
REAL p= 16 s= 16 signed | DB=BigNumber(16,16)
VMB=BigNumber(16,16) PRV=BigNumber(16,16) NEW=BigNumber(16,16)
REAL p= 126 s= 0 signed | DB=Number(-1,-1) VMB=Number(-1,-1)
PRV=Number(-1,-1) NEW=Number(-1,-1)
TIMESTAMP p= 0 s= 0 signed | DB=Timestamp(0,-1)
VMB=Timestamp(0,-1) PRV=Timestamp(0,-1) NEW=Timestamp(0,-1)
@@ -207,31 +207,31 @@ TINYINT p= 16 s= 16 signed | DB=Integer(2,0)
VMB=Integer(2,0)
TINYINT p= 126 s= 0 signed | DB=Integer(2,0) VMB=Integer(2,0)
PRV=Integer(2,0) NEW=Integer(2,0)
DECIMAL p= 0 s= 0 signed | DB=Number(0,0) VMB=Number(0,0)
PRV=Number(0,0) NEW=Number(0,0)
DECIMAL p= 38 s= 0 signed | DB=BigNumber(38,0)
VMB=BigNumber(38,0) PRV=BigNumber(38,0) NEW=BigNumber(38,0)
-DECIMAL p= 10 s= 2 signed | DB=Number(8,2) VMB=Number(10,2)
PRV=Number(10,2) NEW=Number(8,2) DIVERGE
-DECIMAL p= 20 s= 10 signed | DB=Number(10,10)
VMB=BigNumber(20,10) PRV=BigNumber(20,10) NEW=Number(10,10) DIVERGE
+DECIMAL p= 10 s= 2 signed | DB=Number(10,2) VMB=Number(10,2)
PRV=Number(10,2) NEW=Number(10,2)
+DECIMAL p= 20 s= 10 signed | DB=BigNumber(20,10)
VMB=BigNumber(20,10) PRV=BigNumber(20,10) NEW=BigNumber(20,10)
DECIMAL p= 16 s= 16 signed | DB=BigNumber(16,16)
VMB=BigNumber(16,16) PRV=BigNumber(16,16) NEW=BigNumber(16,16)
DECIMAL p= 126 s= 0 signed | DB=Number(-1,0) VMB=Number(-1,0)
PRV=Number(-1,0) NEW=Number(-1,0)
NUMERIC p= 0 s= 0 signed | DB=Number(0,0) VMB=Number(0,0)
PRV=Number(0,0) NEW=Number(0,0)
NUMERIC p= 38 s= 0 signed | DB=BigNumber(38,0)
VMB=BigNumber(38,0) PRV=BigNumber(38,0) NEW=BigNumber(38,0)
-NUMERIC p= 10 s= 2 signed | DB=Number(8,2) VMB=Number(10,2)
PRV=Number(10,2) NEW=Number(8,2) DIVERGE
-NUMERIC p= 20 s= 10 signed | DB=Number(10,10)
VMB=BigNumber(20,10) PRV=BigNumber(20,10) NEW=Number(10,10) DIVERGE
+NUMERIC p= 10 s= 2 signed | DB=Number(10,2) VMB=Number(10,2)
PRV=Number(10,2) NEW=Number(10,2)
+NUMERIC p= 20 s= 10 signed | DB=BigNumber(20,10)
VMB=BigNumber(20,10) PRV=BigNumber(20,10) NEW=BigNumber(20,10)
NUMERIC p= 16 s= 16 signed | DB=BigNumber(16,16)
VMB=BigNumber(16,16) PRV=BigNumber(16,16) NEW=BigNumber(16,16)
NUMERIC p= 126 s= 0 signed | DB=Number(-1,0) VMB=Number(-1,0)
PRV=Number(-1,0) NEW=Number(-1,0)
DOUBLE p= 0 s= 0 signed | DB=Number(0,-1) VMB=Number(0,-1)
PRV=Number(0,-1) NEW=Number(0,-1)
DOUBLE p= 38 s= 0 signed | DB=BigNumber(38,-1)
VMB=BigNumber(38,-1) PRV=Number(-1,-1) NEW=BigNumber(38,-1) DIVERGE
-DOUBLE p= 10 s= 2 signed | DB=Number(8,2) VMB=Number(10,2)
PRV=Number(10,2) NEW=Number(8,2) DIVERGE
+DOUBLE p= 10 s= 2 signed | DB=Number(10,2) VMB=Number(10,2)
PRV=Number(10,2) NEW=Number(10,2)
DOUBLE p= 20 s= 10 signed | DB=Number(-1,-1)
VMB=BigNumber(20,10) PRV=Number(-1,10) NEW=Number(-1,-1) DIVERGE
DOUBLE p= 16 s= 16 signed | DB=Number(-1,-1) VMB=Number(-1,-1)
PRV=Number(-1,-1) NEW=Number(-1,-1)
DOUBLE p= 126 s= 0 signed | DB=Number(-1,-1) VMB=Number(-1,-1)
PRV=Number(-1,-1) NEW=Number(-1,-1)
FLOAT p= 0 s= 0 signed | DB=Number(0,-1) VMB=Number(0,-1)
PRV=Number(0,-1) NEW=Number(0,-1)
FLOAT p= 38 s= 0 signed | DB=BigNumber(38,-1)
VMB=BigNumber(38,-1) PRV=BigNumber(38,-1) NEW=BigNumber(38,-1)
-FLOAT p= 10 s= 2 signed | DB=Number(8,2) VMB=Number(10,2)
PRV=Number(10,2) NEW=Number(8,2) DIVERGE
+FLOAT p= 10 s= 2 signed | DB=Number(10,2) VMB=Number(10,2)
PRV=Number(10,2) NEW=Number(10,2)
FLOAT p= 20 s= 10 signed | DB=Number(-1,-1)
VMB=BigNumber(20,10) PRV=BigNumber(20,10) NEW=Number(-1,-1) DIVERGE
FLOAT p= 16 s= 16 signed | DB=Number(-1,-1) VMB=Number(-1,-1)
PRV=Number(-1,-1) NEW=Number(-1,-1)
FLOAT p= 126 s= 0 signed | DB=Number(-1,-1) VMB=Number(-1,-1)
PRV=Number(-1,-1) NEW=Number(-1,-1)
REAL p= 0 s= 0 signed | DB=Number(0,-1) VMB=Number(0,-1)
PRV=Number(0,-1) NEW=Number(0,-1)
REAL p= 38 s= 0 signed | DB=BigNumber(38,-1)
VMB=BigNumber(38,-1) PRV=BigNumber(38,-1) NEW=BigNumber(38,-1)
-REAL p= 10 s= 2 signed | DB=Number(8,2) VMB=Number(10,2)
PRV=Number(10,2) NEW=Number(8,2) DIVERGE
+REAL p= 10 s= 2 signed | DB=Number(10,2) VMB=Number(10,2)
PRV=Number(10,2) NEW=Number(10,2)
REAL p= 20 s= 10 signed | DB=Number(-1,-1)
VMB=BigNumber(20,10) PRV=BigNumber(20,10) NEW=Number(-1,-1) DIVERGE
REAL p= 16 s= 16 signed | DB=Number(-1,-1) VMB=Number(-1,-1)
PRV=Number(-1,-1) NEW=Number(-1,-1)
REAL p= 126 s= 0 signed | DB=Number(-1,-1) VMB=Number(-1,-1)
PRV=Number(-1,-1) NEW=Number(-1,-1)
@@ -355,32 +355,32 @@ TINYINT p= 16 s= 16 signed | DB=Integer(2,0)
VMB=Integer(2,0)
TINYINT p= 126 s= 0 signed | DB=Integer(2,0) VMB=Integer(2,0)
PRV=Integer(2,0) NEW=Integer(2,0)
DECIMAL p= 0 s= 0 signed | DB=Number(0,0) VMB=Number(0,0)
PRV=Number(0,0) NEW=Number(0,0)
DECIMAL p= 38 s= 0 signed | DB=BigNumber(38,0)
VMB=BigNumber(38,0) PRV=BigNumber(38,0) NEW=BigNumber(38,0)
-DECIMAL p= 10 s= 2 signed | DB=Number(8,2) VMB=Number(10,2)
PRV=Number(10,2) NEW=Number(8,2) DIVERGE
-DECIMAL p= 20 s= 10 signed | DB=Number(10,10)
VMB=BigNumber(20,10) PRV=BigNumber(20,10) NEW=Number(10,10) DIVERGE
+DECIMAL p= 10 s= 2 signed | DB=Number(10,2) VMB=Number(10,2)
PRV=Number(10,2) NEW=Number(10,2)
+DECIMAL p= 20 s= 10 signed | DB=BigNumber(20,10)
VMB=BigNumber(20,10) PRV=BigNumber(20,10) NEW=BigNumber(20,10)
DECIMAL p= 16 s= 16 signed | DB=BigNumber(16,16)
VMB=BigNumber(16,16) PRV=BigNumber(16,16) NEW=BigNumber(16,16)
DECIMAL p= 126 s= 0 signed | DB=Number(-1,0) VMB=Number(-1,0)
PRV=Number(-1,0) NEW=Number(-1,0)
NUMERIC p= 0 s= 0 signed | DB=Number(0,0) VMB=Number(0,0)
PRV=Number(0,0) NEW=Number(0,0)
NUMERIC p= 38 s= 0 signed | DB=BigNumber(38,0)
VMB=BigNumber(38,0) PRV=BigNumber(38,0) NEW=BigNumber(38,0)
-NUMERIC p= 10 s= 2 signed | DB=Number(8,2) VMB=Number(10,2)
PRV=Number(10,2) NEW=Number(8,2) DIVERGE
-NUMERIC p= 20 s= 10 signed | DB=Number(10,10)
VMB=BigNumber(20,10) PRV=BigNumber(20,10) NEW=Number(10,10) DIVERGE
+NUMERIC p= 10 s= 2 signed | DB=Number(10,2) VMB=Number(10,2)
PRV=Number(10,2) NEW=Number(10,2)
+NUMERIC p= 20 s= 10 signed | DB=BigNumber(20,10)
VMB=BigNumber(20,10) PRV=BigNumber(20,10) NEW=BigNumber(20,10)
NUMERIC p= 16 s= 16 signed | DB=BigNumber(16,16)
VMB=BigNumber(16,16) PRV=BigNumber(16,16) NEW=BigNumber(16,16)
NUMERIC p= 126 s= 0 signed | DB=Number(-1,0) VMB=Number(-1,0)
PRV=Number(-1,0) NEW=Number(-1,0)
DOUBLE p= 0 s= 0 signed | DB=Number(0,-1) VMB=Number(0,-1)
PRV=Number(0,-1) NEW=Number(0,-1)
DOUBLE p= 38 s= 0 signed | DB=BigNumber(38,-1)
VMB=BigNumber(38,-1) PRV=BigNumber(38,-1) NEW=BigNumber(38,-1)
-DOUBLE p= 10 s= 2 signed | DB=Number(8,2) VMB=Number(10,2)
PRV=Number(10,2) NEW=Number(8,2) DIVERGE
-DOUBLE p= 20 s= 10 signed | DB=Number(10,10)
VMB=BigNumber(20,10) PRV=BigNumber(20,10) NEW=Number(10,10) DIVERGE
+DOUBLE p= 10 s= 2 signed | DB=Number(10,2) VMB=Number(10,2)
PRV=Number(10,2) NEW=Number(10,2)
+DOUBLE p= 20 s= 10 signed | DB=BigNumber(20,10)
VMB=BigNumber(20,10) PRV=BigNumber(20,10) NEW=BigNumber(20,10)
DOUBLE p= 16 s= 16 signed | DB=BigNumber(16,16)
VMB=BigNumber(16,16) PRV=BigNumber(16,16) NEW=BigNumber(16,16)
DOUBLE p= 126 s= 0 signed | DB=Number(-1,-1) VMB=Number(-1,-1)
PRV=Number(-1,-1) NEW=Number(-1,-1)
FLOAT p= 0 s= 0 signed | DB=Number(0,-1) VMB=Number(0,-1)
PRV=Number(0,-1) NEW=Number(0,-1)
FLOAT p= 38 s= 0 signed | DB=BigNumber(38,-1)
VMB=BigNumber(38,-1) PRV=BigNumber(38,-1) NEW=BigNumber(38,-1)
-FLOAT p= 10 s= 2 signed | DB=Number(8,2) VMB=Number(10,2)
PRV=Number(10,2) NEW=Number(8,2) DIVERGE
-FLOAT p= 20 s= 10 signed | DB=Number(10,10)
VMB=BigNumber(20,10) PRV=BigNumber(20,10) NEW=Number(10,10) DIVERGE
+FLOAT p= 10 s= 2 signed | DB=Number(10,2) VMB=Number(10,2)
PRV=Number(10,2) NEW=Number(10,2)
+FLOAT p= 20 s= 10 signed | DB=BigNumber(20,10)
VMB=BigNumber(20,10) PRV=BigNumber(20,10) NEW=BigNumber(20,10)
FLOAT p= 16 s= 16 signed | DB=BigNumber(16,16)
VMB=BigNumber(16,16) PRV=BigNumber(16,16) NEW=BigNumber(16,16)
FLOAT p= 126 s= 0 signed | DB=Number(-1,-1) VMB=Number(-1,-1)
PRV=Number(-1,-1) NEW=Number(-1,-1)
REAL p= 0 s= 0 signed | DB=Number(0,-1) VMB=Number(0,-1)
PRV=Number(0,-1) NEW=Number(0,-1)
REAL p= 38 s= 0 signed | DB=BigNumber(38,-1)
VMB=BigNumber(38,-1) PRV=BigNumber(38,-1) NEW=BigNumber(38,-1)
-REAL p= 10 s= 2 signed | DB=Number(8,2) VMB=Number(10,2)
PRV=Number(10,2) NEW=Number(8,2) DIVERGE
-REAL p= 20 s= 10 signed | DB=Number(10,10)
VMB=BigNumber(20,10) PRV=BigNumber(20,10) NEW=Number(10,10) DIVERGE
+REAL p= 10 s= 2 signed | DB=Number(10,2) VMB=Number(10,2)
PRV=Number(10,2) NEW=Number(10,2)
+REAL p= 20 s= 10 signed | DB=BigNumber(20,10)
VMB=BigNumber(20,10) PRV=BigNumber(20,10) NEW=BigNumber(20,10)
REAL p= 16 s= 16 signed | DB=BigNumber(16,16)
VMB=BigNumber(16,16) PRV=BigNumber(16,16) NEW=BigNumber(16,16)
REAL p= 126 s= 0 signed | DB=Number(-1,-1) VMB=Number(-1,-1)
PRV=Number(-1,-1) NEW=Number(-1,-1)
TIMESTAMP p= 0 s= 0 signed | DB=Timestamp(0,-1)
VMB=Timestamp(0,-1) PRV=Timestamp(0,-1) NEW=Timestamp(0,-1)
@@ -503,32 +503,32 @@ TINYINT p= 16 s= 16 signed | DB=Integer(2,0)
VMB=Integer(2,0)
TINYINT p= 126 s= 0 signed | DB=Integer(2,0) VMB=Integer(2,0)
PRV=Integer(2,0) NEW=Integer(2,0)
DECIMAL p= 0 s= 0 signed | DB=Number(0,0) VMB=Number(0,0)
PRV=Number(0,0) NEW=Number(0,0)
DECIMAL p= 38 s= 0 signed | DB=BigNumber(38,0)
VMB=BigNumber(38,0) PRV=BigNumber(38,0) NEW=BigNumber(38,0)
-DECIMAL p= 10 s= 2 signed | DB=Number(8,2) VMB=Number(10,2)
PRV=Number(10,2) NEW=Number(8,2) DIVERGE
-DECIMAL p= 20 s= 10 signed | DB=Number(10,10)
VMB=BigNumber(20,10) PRV=BigNumber(20,10) NEW=Number(10,10) DIVERGE
+DECIMAL p= 10 s= 2 signed | DB=Number(10,2) VMB=Number(10,2)
PRV=Number(10,2) NEW=Number(10,2)
+DECIMAL p= 20 s= 10 signed | DB=BigNumber(20,10)
VMB=BigNumber(20,10) PRV=BigNumber(20,10) NEW=BigNumber(20,10)
DECIMAL p= 16 s= 16 signed | DB=BigNumber(16,16)
VMB=BigNumber(16,16) PRV=BigNumber(16,16) NEW=BigNumber(16,16)
DECIMAL p= 126 s= 0 signed | DB=Number(-1,0) VMB=Number(-1,0)
PRV=Number(-1,0) NEW=Number(-1,0)
NUMERIC p= 0 s= 0 signed | DB=Number(0,0) VMB=Number(0,0)
PRV=Number(0,0) NEW=Number(0,0)
NUMERIC p= 38 s= 0 signed | DB=BigNumber(38,0)
VMB=BigNumber(38,0) PRV=BigNumber(38,0) NEW=BigNumber(38,0)
-NUMERIC p= 10 s= 2 signed | DB=Number(8,2) VMB=Number(10,2)
PRV=Number(10,2) NEW=Number(8,2) DIVERGE
-NUMERIC p= 20 s= 10 signed | DB=Number(10,10)
VMB=BigNumber(20,10) PRV=BigNumber(20,10) NEW=Number(10,10) DIVERGE
+NUMERIC p= 10 s= 2 signed | DB=Number(10,2) VMB=Number(10,2)
PRV=Number(10,2) NEW=Number(10,2)
+NUMERIC p= 20 s= 10 signed | DB=BigNumber(20,10)
VMB=BigNumber(20,10) PRV=BigNumber(20,10) NEW=BigNumber(20,10)
NUMERIC p= 16 s= 16 signed | DB=BigNumber(16,16)
VMB=BigNumber(16,16) PRV=BigNumber(16,16) NEW=BigNumber(16,16)
NUMERIC p= 126 s= 0 signed | DB=Number(-1,0) VMB=Number(-1,0)
PRV=Number(-1,0) NEW=Number(-1,0)
DOUBLE p= 0 s= 0 signed | DB=Number(0,-1) VMB=Number(0,-1)
PRV=Number(0,-1) NEW=Number(0,-1)
DOUBLE p= 38 s= 0 signed | DB=BigNumber(38,-1)
VMB=BigNumber(38,-1) PRV=BigNumber(38,-1) NEW=BigNumber(38,-1)
-DOUBLE p= 10 s= 2 signed | DB=Number(8,2) VMB=Number(10,2)
PRV=Number(10,2) NEW=Number(8,2) DIVERGE
-DOUBLE p= 20 s= 10 signed | DB=Number(10,10)
VMB=BigNumber(20,10) PRV=BigNumber(20,10) NEW=Number(10,10) DIVERGE
+DOUBLE p= 10 s= 2 signed | DB=Number(10,2) VMB=Number(10,2)
PRV=Number(10,2) NEW=Number(10,2)
+DOUBLE p= 20 s= 10 signed | DB=BigNumber(20,10)
VMB=BigNumber(20,10) PRV=BigNumber(20,10) NEW=BigNumber(20,10)
DOUBLE p= 16 s= 16 signed | DB=BigNumber(16,16)
VMB=BigNumber(16,16) PRV=BigNumber(16,16) NEW=BigNumber(16,16)
DOUBLE p= 126 s= 0 signed | DB=Number(-1,-1) VMB=Number(-1,-1)
PRV=Number(-1,-1) NEW=Number(-1,-1)
FLOAT p= 0 s= 0 signed | DB=Number(0,-1) VMB=Number(0,-1)
PRV=Number(0,-1) NEW=Number(0,-1)
FLOAT p= 38 s= 0 signed | DB=BigNumber(38,-1)
VMB=BigNumber(38,-1) PRV=BigNumber(38,-1) NEW=BigNumber(38,-1)
-FLOAT p= 10 s= 2 signed | DB=Number(8,2) VMB=Number(10,2)
PRV=Number(10,2) NEW=Number(8,2) DIVERGE
-FLOAT p= 20 s= 10 signed | DB=Number(10,10)
VMB=BigNumber(20,10) PRV=BigNumber(20,10) NEW=Number(10,10) DIVERGE
+FLOAT p= 10 s= 2 signed | DB=Number(10,2) VMB=Number(10,2)
PRV=Number(10,2) NEW=Number(10,2)
+FLOAT p= 20 s= 10 signed | DB=BigNumber(20,10)
VMB=BigNumber(20,10) PRV=BigNumber(20,10) NEW=BigNumber(20,10)
FLOAT p= 16 s= 16 signed | DB=BigNumber(16,16)
VMB=BigNumber(16,16) PRV=BigNumber(16,16) NEW=BigNumber(16,16)
FLOAT p= 126 s= 0 signed | DB=Number(-1,-1) VMB=Number(-1,-1)
PRV=Number(-1,-1) NEW=Number(-1,-1)
REAL p= 0 s= 0 signed | DB=Number(0,-1) VMB=Number(0,-1)
PRV=Number(0,-1) NEW=Number(0,-1)
REAL p= 38 s= 0 signed | DB=BigNumber(38,-1)
VMB=BigNumber(38,-1) PRV=BigNumber(38,-1) NEW=BigNumber(38,-1)
-REAL p= 10 s= 2 signed | DB=Number(8,2) VMB=Number(10,2)
PRV=Number(10,2) NEW=Number(8,2) DIVERGE
-REAL p= 20 s= 10 signed | DB=Number(10,10)
VMB=BigNumber(20,10) PRV=BigNumber(20,10) NEW=Number(10,10) DIVERGE
+REAL p= 10 s= 2 signed | DB=Number(10,2) VMB=Number(10,2)
PRV=Number(10,2) NEW=Number(10,2)
+REAL p= 20 s= 10 signed | DB=BigNumber(20,10)
VMB=BigNumber(20,10) PRV=BigNumber(20,10) NEW=BigNumber(20,10)
REAL p= 16 s= 16 signed | DB=BigNumber(16,16)
VMB=BigNumber(16,16) PRV=BigNumber(16,16) NEW=BigNumber(16,16)
REAL p= 126 s= 0 signed | DB=Number(-1,-1) VMB=Number(-1,-1)
PRV=Number(-1,-1) NEW=Number(-1,-1)
TIMESTAMP p= 0 s= 0 signed | DB=Timestamp(0,-1)
VMB=Timestamp(0,-1) PRV=Timestamp(0,-1) NEW=Timestamp(0,-1)
@@ -651,32 +651,32 @@ TINYINT p= 16 s= 16 signed | DB=Integer(2,0)
VMB=Integer(2,0)
TINYINT p= 126 s= 0 signed | DB=Integer(2,0) VMB=Integer(2,0)
PRV=Integer(2,0) NEW=Integer(2,0)
DECIMAL p= 0 s= 0 signed | DB=Number(0,0) VMB=Number(0,0)
PRV=Number(0,0) NEW=Number(0,0)
DECIMAL p= 38 s= 0 signed | DB=BigNumber(38,0)
VMB=BigNumber(38,0) PRV=BigNumber(38,0) NEW=BigNumber(38,0)
-DECIMAL p= 10 s= 2 signed | DB=Number(8,2) VMB=Number(10,2)
PRV=Number(10,2) NEW=Number(8,2) DIVERGE
-DECIMAL p= 20 s= 10 signed | DB=Number(10,10)
VMB=BigNumber(20,10) PRV=BigNumber(20,10) NEW=Number(10,10) DIVERGE
+DECIMAL p= 10 s= 2 signed | DB=Number(10,2) VMB=Number(10,2)
PRV=Number(10,2) NEW=Number(10,2)
+DECIMAL p= 20 s= 10 signed | DB=BigNumber(20,10)
VMB=BigNumber(20,10) PRV=BigNumber(20,10) NEW=BigNumber(20,10)
DECIMAL p= 16 s= 16 signed | DB=BigNumber(16,16)
VMB=BigNumber(16,16) PRV=BigNumber(16,16) NEW=BigNumber(16,16)
DECIMAL p= 126 s= 0 signed | DB=Number(-1,0) VMB=Number(-1,0)
PRV=Number(-1,0) NEW=Number(-1,0)
NUMERIC p= 0 s= 0 signed | DB=Number(0,0) VMB=Number(0,0)
PRV=Number(0,0) NEW=Number(0,0)
NUMERIC p= 38 s= 0 signed | DB=BigNumber(38,0)
VMB=BigNumber(38,0) PRV=BigNumber(38,0) NEW=BigNumber(38,0)
-NUMERIC p= 10 s= 2 signed | DB=Number(8,2) VMB=Number(10,2)
PRV=Number(10,2) NEW=Number(8,2) DIVERGE
-NUMERIC p= 20 s= 10 signed | DB=Number(10,10)
VMB=BigNumber(20,10) PRV=BigNumber(20,10) NEW=Number(10,10) DIVERGE
+NUMERIC p= 10 s= 2 signed | DB=Number(10,2) VMB=Number(10,2)
PRV=Number(10,2) NEW=Number(10,2)
+NUMERIC p= 20 s= 10 signed | DB=BigNumber(20,10)
VMB=BigNumber(20,10) PRV=BigNumber(20,10) NEW=BigNumber(20,10)
NUMERIC p= 16 s= 16 signed | DB=BigNumber(16,16)
VMB=BigNumber(16,16) PRV=BigNumber(16,16) NEW=BigNumber(16,16)
NUMERIC p= 126 s= 0 signed | DB=Number(-1,0) VMB=Number(-1,0)
PRV=Number(-1,0) NEW=Number(-1,0)
DOUBLE p= 0 s= 0 signed | DB=Number(0,-1) VMB=Number(0,-1)
PRV=Number(0,-1) NEW=Number(0,-1)
DOUBLE p= 38 s= 0 signed | DB=BigNumber(38,-1)
VMB=BigNumber(38,-1) PRV=BigNumber(38,-1) NEW=BigNumber(38,-1)
-DOUBLE p= 10 s= 2 signed | DB=Number(8,2) VMB=Number(10,2)
PRV=Number(10,2) NEW=Number(8,2) DIVERGE
-DOUBLE p= 20 s= 10 signed | DB=Number(10,10)
VMB=BigNumber(20,10) PRV=BigNumber(20,10) NEW=Number(10,10) DIVERGE
+DOUBLE p= 10 s= 2 signed | DB=Number(10,2) VMB=Number(10,2)
PRV=Number(10,2) NEW=Number(10,2)
+DOUBLE p= 20 s= 10 signed | DB=BigNumber(20,10)
VMB=BigNumber(20,10) PRV=BigNumber(20,10) NEW=BigNumber(20,10)
DOUBLE p= 16 s= 16 signed | DB=BigNumber(16,16)
VMB=BigNumber(16,16) PRV=BigNumber(16,16) NEW=BigNumber(16,16)
DOUBLE p= 126 s= 0 signed | DB=Number(-1,-1) VMB=Number(-1,-1)
PRV=Number(-1,-1) NEW=Number(-1,-1)
FLOAT p= 0 s= 0 signed | DB=Number(0,-1) VMB=Number(0,-1)
PRV=Number(0,-1) NEW=Number(0,-1)
FLOAT p= 38 s= 0 signed | DB=BigNumber(38,-1)
VMB=BigNumber(38,-1) PRV=BigNumber(38,-1) NEW=BigNumber(38,-1)
-FLOAT p= 10 s= 2 signed | DB=Number(8,2) VMB=Number(10,2)
PRV=Number(10,2) NEW=Number(8,2) DIVERGE
-FLOAT p= 20 s= 10 signed | DB=Number(10,10)
VMB=BigNumber(20,10) PRV=BigNumber(20,10) NEW=Number(10,10) DIVERGE
+FLOAT p= 10 s= 2 signed | DB=Number(10,2) VMB=Number(10,2)
PRV=Number(10,2) NEW=Number(10,2)
+FLOAT p= 20 s= 10 signed | DB=BigNumber(20,10)
VMB=BigNumber(20,10) PRV=BigNumber(20,10) NEW=BigNumber(20,10)
FLOAT p= 16 s= 16 signed | DB=BigNumber(16,16)
VMB=BigNumber(16,16) PRV=BigNumber(16,16) NEW=BigNumber(16,16)
FLOAT p= 126 s= 0 signed | DB=Number(-1,-1) VMB=Number(-1,-1)
PRV=Number(-1,-1) NEW=Number(-1,-1)
REAL p= 0 s= 0 signed | DB=Number(0,-1) VMB=Number(0,-1)
PRV=Number(0,-1) NEW=Number(0,-1)
REAL p= 38 s= 0 signed | DB=BigNumber(38,-1)
VMB=BigNumber(38,-1) PRV=BigNumber(38,-1) NEW=BigNumber(38,-1)
-REAL p= 10 s= 2 signed | DB=Number(8,2) VMB=Number(10,2)
PRV=Number(10,2) NEW=Number(8,2) DIVERGE
-REAL p= 20 s= 10 signed | DB=Number(10,10)
VMB=BigNumber(20,10) PRV=BigNumber(20,10) NEW=Number(10,10) DIVERGE
+REAL p= 10 s= 2 signed | DB=Number(10,2) VMB=Number(10,2)
PRV=Number(10,2) NEW=Number(10,2)
+REAL p= 20 s= 10 signed | DB=BigNumber(20,10)
VMB=BigNumber(20,10) PRV=BigNumber(20,10) NEW=BigNumber(20,10)
REAL p= 16 s= 16 signed | DB=BigNumber(16,16)
VMB=BigNumber(16,16) PRV=BigNumber(16,16) NEW=BigNumber(16,16)
REAL p= 126 s= 0 signed | DB=Number(-1,-1) VMB=Number(-1,-1)
PRV=Number(-1,-1) NEW=Number(-1,-1)
TIMESTAMP p= 0 s= 0 signed | DB=Date(-1,-1) VMB=None(-1,-1)
PRV=None(-1,-1) NEW=Date(-1,-1) DIVERGE
@@ -748,4 +748,4 @@ STRUCT p= 126 s= 0 signed | DB=String(-1,-1)
VMB=String(-1,-1)
BIGINT p= 20 s= 0 unsigned | DB=BigNumber(16,0)
VMB=BigNumber(16,0) PRV=Integer(15,0) NEW=BigNumber(16,0) DIVERGE
YEAR p= 4 s= 0 signed | DB=Date(-1,-1) VMB=Date(-1,-1)
PRV=Date(-1,-1) NEW=Date(-1,-1)
-# diverging cases: 62
+# diverging cases: 15
diff --git a/docs/hop-dev-manual/modules/ROOT/pages/database/column-types.adoc
b/docs/hop-dev-manual/modules/ROOT/pages/database/column-types.adoc
index ed14dbc521..f58e763d2e 100644
--- a/docs/hop-dev-manual/modules/ROOT/pages/database/column-types.adoc
+++ b/docs/hop-dev-manual/modules/ROOT/pages/database/column-types.adoc
@@ -45,10 +45,14 @@ Hop provides the plain JDBC behaviour; a dialect declares
the parts it does diff
Two of these carry a meaning that is easy to get wrong.
-`length` on a numeric type is the number of digits *before* the decimal, not
the total.
-A database reports the total, so the scale has to come off.
-`NUMERIC(10,2)` is length 8, precision 2, and is written back as `NUMERIC(8+2,
2)`.
-Getting this backwards makes a column grow by its scale on every round trip.
+`length` on a numeric type is the *total* number of significant digits, the
same value the database reports as precision.
+`NUMERIC(10,2)` is length 10, precision 2, and is written back as `NUMERIC(10,
2)` (or `DECIMAL(10,2)`).
+`numericLength()` on a `DatabaseColumn` is the digits before the decimal (8
for that example) and is only for dialect rules that test whether a declaration
is possible.
+
+PostgreSQL used to emit `NUMERIC(length+precision, precision)` when Hop length
still meant integer digits.
+From 2.20 it emits `NUMERIC(length, precision)`.
+A hand-authored Number field of length 10, precision 3 therefore generates
`NUMERIC(10, 3)` rather than `NUMERIC(13, 3)`.
+If scale is larger than length, precision is widened so PostgreSQL before 15
does not reject the statement.
`precision` of exactly 1 on a `TYPE_DATE` is a marker meaning the value is a
date rather than a timestamp.
It is not a digit count.
@@ -234,7 +238,8 @@ What survives when no rule claims a column is
`StandardJdbcTypeMapper`, which do
It exposes a few helpers that rules can use so that a condition does not have
to restate arithmetic the mapping already does:
-* `numericLength(column)` — digits before the decimal, after the scale has
been taken off
+* `numericLength(column)` — digits before the decimal, after the scale has
been taken off. Dialect rules use this to test whether a declaration is
possible at all.
+* `totalDigits(column)` — the total number of significant digits (JDBC
precision). This is what Hop stores as Number/BigNumber length and what
`DECIMAL(length, precision)` writers emit.
* `numericScale(column)` — digits after the decimal
* `displaySizeIsTwiceThePrecision(databaseMeta, column)` — the `CHAR(X) FOR
BIT DATA` shape
diff --git
a/docs/hop-user-manual/modules/ROOT/pages/database/databases/postgresql.adoc
b/docs/hop-user-manual/modules/ROOT/pages/database/databases/postgresql.adoc
index fe93bf3f99..f7b0bc3b04 100644
--- a/docs/hop-user-manual/modules/ROOT/pages/database/databases/postgresql.adoc
+++ b/docs/hop-user-manual/modules/ROOT/pages/database/databases/postgresql.adoc
@@ -30,3 +30,11 @@ under the License.
|Documentation |
https://jdbc.postgresql.org/documentation/head/index.html[Documentation Link]
|JDBC Url | jdbc:postgresql://host:port/database
|===
+
+== Numeric DDL in 2.20
+
+Hop Number/BigNumber *length* is the total number of significant digits (the
database precision), not the digits before the decimal.
+
+Table Output and similar DDL therefore emit `NUMERIC(length, scale)`.
+A field authored as length 10, precision 3 generates `NUMERIC(10, 3)`.
+In 2.19 and earlier the PostgreSQL dialect added length and scale and emitted
`NUMERIC(13, 3)` for that same metadata.
diff --git
a/plugins/databases/cockroachdb/src/test/resources/org/apache/hop/databases/cockroachdb/CockroachFieldDefinitionGoldenTest.txt
b/plugins/databases/cockroachdb/src/test/resources/org/apache/hop/databases/cockroachdb/CockroachFieldDefinitionGoldenTest.txt
index 98d949d912..4742377a28 100644
---
a/plugins/databases/cockroachdb/src/test/resources/org/apache/hop/databases/cockroachdb/CockroachFieldDefinitionGoldenTest.txt
+++
b/plugins/databases/cockroachdb/src/test/resources/org/apache/hop/databases/cockroachdb/CockroachFieldDefinitionGoldenTest.txt
@@ -25,7 +25,7 @@ NUMBER len= -1 prec= -1 - -> DOUBLE
PRECISION
NUMBER len= 0 prec= 0 - -> DOUBLE PRECISION
NUMBER len= 4 prec= 0 - -> SMALLINT
NUMBER len= 9 prec= 0 - -> INTEGER
-NUMBER len= 10 prec= 2 - -> NUMERIC(12, 2)
+NUMBER len= 10 prec= 2 - -> NUMERIC(10, 2)
NUMBER len= 15 prec= 0 - -> BIGINT
NUMBER len= 18 prec= 0 - -> BIGINT
NUMBER len= 20 prec= 0 - -> NUMERIC(20, 0)
@@ -34,7 +34,7 @@ BIGNUMBER len= -1 prec= -1 - -> DOUBLE
PRECISION
BIGNUMBER len= 0 prec= 0 - -> DOUBLE PRECISION
BIGNUMBER len= 4 prec= 0 - -> SMALLINT
BIGNUMBER len= 9 prec= 0 - -> INTEGER
-BIGNUMBER len= 10 prec= 2 - -> NUMERIC(12, 2)
+BIGNUMBER len= 10 prec= 2 - -> NUMERIC(10, 2)
BIGNUMBER len= 15 prec= 0 - -> BIGINT
BIGNUMBER len= 18 prec= 0 - -> BIGINT
BIGNUMBER len= 20 prec= 0 - -> NUMERIC(20, 0)
@@ -232,14 +232,14 @@ INTEGER len= 15 prec= 0 add -> ALTER
TABLE TBL ADD COLUMN COL B
INTEGER len= 15 prec= 0 modify -> ALTER TABLE TBL ADD COLUMN
COL_KTL BIGINT; UPDATE TBL SET COL_KTL=COL; ALTER TABLE TBL DROP COLUMN COL;
ALTER TABLE TBL RENAME COL_KTL TO COL;
NUMBER len= -1 prec= -1 add -> ALTER TABLE TBL ADD COLUMN COL
DOUBLE PRECISION
NUMBER len= -1 prec= -1 modify -> ALTER TABLE TBL ADD COLUMN
COL_KTL DOUBLE PRECISION; UPDATE TBL SET COL_KTL=COL; ALTER TABLE TBL DROP
COLUMN COL; ALTER TABLE TBL RENAME COL_KTL TO COL;
-NUMBER len= 10 prec= 2 add -> ALTER TABLE TBL ADD COLUMN COL
NUMERIC(12, 2)
-NUMBER len= 10 prec= 2 modify -> ALTER TABLE TBL ADD COLUMN
COL_KTL NUMERIC(12, 2); UPDATE TBL SET COL_KTL=COL; ALTER TABLE TBL DROP COLUMN
COL; ALTER TABLE TBL RENAME COL_KTL TO COL;
+NUMBER len= 10 prec= 2 add -> ALTER TABLE TBL ADD COLUMN COL
NUMERIC(10, 2)
+NUMBER len= 10 prec= 2 modify -> ALTER TABLE TBL ADD COLUMN
COL_KTL NUMERIC(10, 2); UPDATE TBL SET COL_KTL=COL; ALTER TABLE TBL DROP COLUMN
COL; ALTER TABLE TBL RENAME COL_KTL TO COL;
NUMBER len= 15 prec= 0 add -> ALTER TABLE TBL ADD COLUMN COL
BIGINT
NUMBER len= 15 prec= 0 modify -> ALTER TABLE TBL ADD COLUMN
COL_KTL BIGINT; UPDATE TBL SET COL_KTL=COL; ALTER TABLE TBL DROP COLUMN COL;
ALTER TABLE TBL RENAME COL_KTL TO COL;
BIGNUMBER len= -1 prec= -1 add -> ALTER TABLE TBL ADD COLUMN COL
DOUBLE PRECISION
BIGNUMBER len= -1 prec= -1 modify -> ALTER TABLE TBL ADD COLUMN
COL_KTL DOUBLE PRECISION; UPDATE TBL SET COL_KTL=COL; ALTER TABLE TBL DROP
COLUMN COL; ALTER TABLE TBL RENAME COL_KTL TO COL;
-BIGNUMBER len= 10 prec= 2 add -> ALTER TABLE TBL ADD COLUMN COL
NUMERIC(12, 2)
-BIGNUMBER len= 10 prec= 2 modify -> ALTER TABLE TBL ADD COLUMN
COL_KTL NUMERIC(12, 2); UPDATE TBL SET COL_KTL=COL; ALTER TABLE TBL DROP COLUMN
COL; ALTER TABLE TBL RENAME COL_KTL TO COL;
+BIGNUMBER len= 10 prec= 2 add -> ALTER TABLE TBL ADD COLUMN COL
NUMERIC(10, 2)
+BIGNUMBER len= 10 prec= 2 modify -> ALTER TABLE TBL ADD COLUMN
COL_KTL NUMERIC(10, 2); UPDATE TBL SET COL_KTL=COL; ALTER TABLE TBL DROP COLUMN
COL; ALTER TABLE TBL RENAME COL_KTL TO COL;
BIGNUMBER len= 15 prec= 0 add -> ALTER TABLE TBL ADD COLUMN COL
BIGINT
BIGNUMBER len= 15 prec= 0 modify -> ALTER TABLE TBL ADD COLUMN
COL_KTL BIGINT; UPDATE TBL SET COL_KTL=COL; ALTER TABLE TBL DROP COLUMN COL;
ALTER TABLE TBL RENAME COL_KTL TO COL;
DATE len= -1 prec= -1 add -> ALTER TABLE TBL ADD COLUMN COL
TIMESTAMP
diff --git
a/plugins/databases/greenplum/src/test/resources/org/apache/hop/databases/greenplum/GreenplumFieldDefinitionGoldenTest.txt
b/plugins/databases/greenplum/src/test/resources/org/apache/hop/databases/greenplum/GreenplumFieldDefinitionGoldenTest.txt
index 79c1b4c2a9..0ad0d9d7d3 100644
---
a/plugins/databases/greenplum/src/test/resources/org/apache/hop/databases/greenplum/GreenplumFieldDefinitionGoldenTest.txt
+++
b/plugins/databases/greenplum/src/test/resources/org/apache/hop/databases/greenplum/GreenplumFieldDefinitionGoldenTest.txt
@@ -25,7 +25,7 @@ NUMBER len= -1 prec= -1 - -> DOUBLE
PRECISION
NUMBER len= 0 prec= 0 - -> DOUBLE PRECISION
NUMBER len= 4 prec= 0 - -> SMALLINT
NUMBER len= 9 prec= 0 - -> INTEGER
-NUMBER len= 10 prec= 2 - -> NUMERIC(12, 2)
+NUMBER len= 10 prec= 2 - -> NUMERIC(10, 2)
NUMBER len= 15 prec= 0 - -> BIGINT
NUMBER len= 18 prec= 0 - -> BIGINT
NUMBER len= 20 prec= 0 - -> NUMERIC(20, 0)
@@ -34,7 +34,7 @@ BIGNUMBER len= -1 prec= -1 - -> DOUBLE
PRECISION
BIGNUMBER len= 0 prec= 0 - -> DOUBLE PRECISION
BIGNUMBER len= 4 prec= 0 - -> SMALLINT
BIGNUMBER len= 9 prec= 0 - -> INTEGER
-BIGNUMBER len= 10 prec= 2 - -> NUMERIC(12, 2)
+BIGNUMBER len= 10 prec= 2 - -> NUMERIC(10, 2)
BIGNUMBER len= 15 prec= 0 - -> BIGINT
BIGNUMBER len= 18 prec= 0 - -> BIGINT
BIGNUMBER len= 20 prec= 0 - -> NUMERIC(20, 0)
@@ -232,14 +232,14 @@ INTEGER len= 15 prec= 0 add -> ALTER
TABLE TBL ADD COLUMN COL B
INTEGER len= 15 prec= 0 modify -> ALTER TABLE TBL ADD COLUMN
COL_KTL BIGINT; UPDATE TBL SET COL_KTL=COL; ALTER TABLE TBL DROP COLUMN COL;
ALTER TABLE TBL RENAME COL_KTL TO COL;
NUMBER len= -1 prec= -1 add -> ALTER TABLE TBL ADD COLUMN COL
DOUBLE PRECISION
NUMBER len= -1 prec= -1 modify -> ALTER TABLE TBL ADD COLUMN
COL_KTL DOUBLE PRECISION; UPDATE TBL SET COL_KTL=COL; ALTER TABLE TBL DROP
COLUMN COL; ALTER TABLE TBL RENAME COL_KTL TO COL;
-NUMBER len= 10 prec= 2 add -> ALTER TABLE TBL ADD COLUMN COL
NUMERIC(12, 2)
-NUMBER len= 10 prec= 2 modify -> ALTER TABLE TBL ADD COLUMN
COL_KTL NUMERIC(12, 2); UPDATE TBL SET COL_KTL=COL; ALTER TABLE TBL DROP COLUMN
COL; ALTER TABLE TBL RENAME COL_KTL TO COL;
+NUMBER len= 10 prec= 2 add -> ALTER TABLE TBL ADD COLUMN COL
NUMERIC(10, 2)
+NUMBER len= 10 prec= 2 modify -> ALTER TABLE TBL ADD COLUMN
COL_KTL NUMERIC(10, 2); UPDATE TBL SET COL_KTL=COL; ALTER TABLE TBL DROP COLUMN
COL; ALTER TABLE TBL RENAME COL_KTL TO COL;
NUMBER len= 15 prec= 0 add -> ALTER TABLE TBL ADD COLUMN COL
BIGINT
NUMBER len= 15 prec= 0 modify -> ALTER TABLE TBL ADD COLUMN
COL_KTL BIGINT; UPDATE TBL SET COL_KTL=COL; ALTER TABLE TBL DROP COLUMN COL;
ALTER TABLE TBL RENAME COL_KTL TO COL;
BIGNUMBER len= -1 prec= -1 add -> ALTER TABLE TBL ADD COLUMN COL
DOUBLE PRECISION
BIGNUMBER len= -1 prec= -1 modify -> ALTER TABLE TBL ADD COLUMN
COL_KTL DOUBLE PRECISION; UPDATE TBL SET COL_KTL=COL; ALTER TABLE TBL DROP
COLUMN COL; ALTER TABLE TBL RENAME COL_KTL TO COL;
-BIGNUMBER len= 10 prec= 2 add -> ALTER TABLE TBL ADD COLUMN COL
NUMERIC(12, 2)
-BIGNUMBER len= 10 prec= 2 modify -> ALTER TABLE TBL ADD COLUMN
COL_KTL NUMERIC(12, 2); UPDATE TBL SET COL_KTL=COL; ALTER TABLE TBL DROP COLUMN
COL; ALTER TABLE TBL RENAME COL_KTL TO COL;
+BIGNUMBER len= 10 prec= 2 add -> ALTER TABLE TBL ADD COLUMN COL
NUMERIC(10, 2)
+BIGNUMBER len= 10 prec= 2 modify -> ALTER TABLE TBL ADD COLUMN
COL_KTL NUMERIC(10, 2); UPDATE TBL SET COL_KTL=COL; ALTER TABLE TBL DROP COLUMN
COL; ALTER TABLE TBL RENAME COL_KTL TO COL;
BIGNUMBER len= 15 prec= 0 add -> ALTER TABLE TBL ADD COLUMN COL
BIGINT
BIGNUMBER len= 15 prec= 0 modify -> ALTER TABLE TBL ADD COLUMN
COL_KTL BIGINT; UPDATE TBL SET COL_KTL=COL; ALTER TABLE TBL DROP COLUMN COL;
ALTER TABLE TBL RENAME COL_KTL TO COL;
DATE len= -1 prec= -1 add -> ALTER TABLE TBL ADD COLUMN COL
TIMESTAMP
diff --git
a/plugins/databases/mssql/src/main/java/org/apache/hop/databases/mssql/MsSqlServerDatabaseMeta.java
b/plugins/databases/mssql/src/main/java/org/apache/hop/databases/mssql/MsSqlServerDatabaseMeta.java
index b017dd5a15..a4b627dabe 100644
---
a/plugins/databases/mssql/src/main/java/org/apache/hop/databases/mssql/MsSqlServerDatabaseMeta.java
+++
b/plugins/databases/mssql/src/main/java/org/apache/hop/databases/mssql/MsSqlServerDatabaseMeta.java
@@ -18,7 +18,9 @@
package org.apache.hop.databases.mssql;
import java.sql.ResultSet;
+import java.sql.Types;
import java.util.List;
+import java.util.Locale;
import org.apache.hop.core.Const;
import org.apache.hop.core.database.BaseDatabaseMeta;
import org.apache.hop.core.database.Database;
@@ -47,6 +49,9 @@ import org.apache.hop.metadata.api.HopMetadataProperty;
@GuiPlugin(id = "GUI-MSSQLServerDatabaseMeta")
public class MsSqlServerDatabaseMeta extends BaseDatabaseMeta implements
IDatabase {
+ /** SQL Server NVARCHAR(n) / NCHAR(n) stop at 4000 UTF-16 code units. */
+ private static final int MAX_NVARCHAR_LENGTH = 4000;
+
private static final List<IDatabaseTypeRule> TYPE_RULES =
DatabaseTypes.rules()
// SQL Server spells a UUID UNIQUEIDENTIFIER, and grew a JSON type
in 2025: a server
@@ -56,6 +61,10 @@ public class MsSqlServerDatabaseMeta extends
BaseDatabaseMeta implements IDataba
.as("UNIQUEIDENTIFIER")
.write(IValueMeta.TYPE_JSON)
.as("JSON")
+ .write(IValueMeta.TYPE_DATE, IValueMeta.TYPE_TIMESTAMP)
+ .as(MsSqlServerDatabaseMeta::dateTimeColumnType)
+ .write(IValueMeta.TYPE_STRING)
+ .as(MsSqlServerDatabaseMeta::stringColumnType)
.build();
@Override
@@ -63,6 +72,71 @@ public class MsSqlServerDatabaseMeta extends
BaseDatabaseMeta implements IDataba
return TYPE_RULES;
}
+ /**
+ * SQL Server date/time spelling. Preserve DATE/TIME/DATETIME2 when the
original JDBC type is
+ * known; new Hop Date fields stay DATETIME, new Timestamp fields become
DATETIME2.
+ */
+ static String dateTimeColumnType(IValueMeta valueMeta) {
+ // Hop type first: a DATE column the user converted to Timestamp must keep
the time.
+ if (valueMeta.getType() == IValueMeta.TYPE_TIMESTAMP
+ ||
"datetime2".equalsIgnoreCase(valueMeta.getOriginalColumnTypeName())) {
+ return "DATETIME2";
+ }
+ int original = valueMeta.getOriginalColumnType();
+ if (original == Types.DATE) {
+ return "DATE";
+ }
+ if (original == Types.TIME) {
+ return "TIME";
+ }
+ return "DATETIME";
+ }
+
+ /**
+ * SQL Server string spelling. National-character source columns stay
NVARCHAR/NCHAR; long strings
+ * use VARCHAR(MAX) rather than the deprecated TEXT type.
+ */
+ static String stringColumnType(IValueMeta valueMeta) {
+ int length = valueMeta.getLength();
+ if (isNationalString(valueMeta)) {
+ if (valueMeta.getOriginalColumnType() == Types.NCHAR
+ && length > 0
+ && length <= MAX_NVARCHAR_LENGTH) {
+ return "NCHAR(" + length + ")";
+ }
+ if (length > 0 && length <= MAX_NVARCHAR_LENGTH) {
+ return "NVARCHAR(" + length + ")";
+ }
+ if (length <= 0) {
+ return "NVARCHAR(100)";
+ }
+ return "NVARCHAR(MAX)";
+ }
+ if (length > 0 && length < 8000) {
+ return "VARCHAR(" + length + ")";
+ }
+ if (length <= 0) {
+ return "VARCHAR(100)";
+ }
+ return "VARCHAR(MAX)";
+ }
+
+ private static boolean isNationalString(IValueMeta valueMeta) {
+ int original = valueMeta.getOriginalColumnType();
+ if (original == Types.NCHAR
+ || original == Types.NVARCHAR
+ || original == Types.LONGNVARCHAR
+ || original == Types.NCLOB) {
+ return true;
+ }
+ String typeName = valueMeta.getOriginalColumnTypeName();
+ if (typeName == null) {
+ return false;
+ }
+ String upper = typeName.toUpperCase(Locale.ROOT);
+ return upper.startsWith("NCHAR") || upper.startsWith("NVARCHAR") ||
upper.startsWith("NTEXT");
+ }
+
/** SQL Server 2025, which is major version 17, is the first with a JSON
type. */
private static final int FIRST_VERSION_WITH_JSON = 17;
@@ -331,7 +405,7 @@ public class MsSqlServerDatabaseMeta extends
BaseDatabaseMeta implements IDataba
int type = v.getType();
switch (type) {
case IValueMeta.TYPE_TIMESTAMP, IValueMeta.TYPE_DATE:
- retval += "DATETIME";
+ retval += dateTimeColumnType(v);
break;
case IValueMeta.TYPE_BOOLEAN:
if (isSupportsBooleanDataType()) {
@@ -371,16 +445,7 @@ public class MsSqlServerDatabaseMeta extends
BaseDatabaseMeta implements IDataba
}
break;
case IValueMeta.TYPE_STRING:
- if (length < getMaxVARCHARLength()) {
- // Maybe use some default DB String length in case length<=0
- if (length > 0) {
- retval += "VARCHAR(" + length + ")";
- } else {
- retval += "VARCHAR(100)";
- }
- } else {
- retval += "TEXT"; // Up to 2bilion characters.
- }
+ retval += stringColumnType(v);
break;
case IValueMeta.TYPE_BINARY:
retval += "VARBINARY(MAX)";
diff --git
a/plugins/databases/mssql/src/test/java/org/apache/hop/databases/mssql/MsSqlServerDatabaseMetaTest.java
b/plugins/databases/mssql/src/test/java/org/apache/hop/databases/mssql/MsSqlServerDatabaseMetaTest.java
index e803c83688..313a26c7fd 100644
---
a/plugins/databases/mssql/src/test/java/org/apache/hop/databases/mssql/MsSqlServerDatabaseMetaTest.java
+++
b/plugins/databases/mssql/src/test/java/org/apache/hop/databases/mssql/MsSqlServerDatabaseMetaTest.java
@@ -501,7 +501,7 @@ class MsSqlServerDatabaseMetaTest {
"ALTER TABLE FOO ADD BAR DATETIME",
nativeMeta.getAddColumnStatement("FOO", new ValueMetaDate("BAR"), "",
false, "", false));
assertEquals(
- "ALTER TABLE FOO ADD BAR DATETIME",
+ "ALTER TABLE FOO ADD BAR DATETIME2",
nativeMeta.getAddColumnStatement(
"FOO", new ValueMetaTimestamp("BAR"), "", false, "", false));
@@ -584,7 +584,7 @@ class MsSqlServerDatabaseMetaTest {
new ValueMetaBigNumber("BAR", 21, 4), "", "", false, false,
false));
assertEquals(
- "TEXT",
+ "VARCHAR(MAX)",
nativeMeta.getFieldDefinition(
new ValueMetaString("BAR", nativeMeta.getMaxVARCHARLength() + 2,
0),
"",
diff --git
a/plugins/databases/mssql/src/test/java/org/apache/hop/databases/mssql/MsSqlServerDatabaseMeta_FieldDefinitionTest.java
b/plugins/databases/mssql/src/test/java/org/apache/hop/databases/mssql/MsSqlServerDatabaseMeta_FieldDefinitionTest.java
index f0f7d8a90a..93c05a5468 100644
---
a/plugins/databases/mssql/src/test/java/org/apache/hop/databases/mssql/MsSqlServerDatabaseMeta_FieldDefinitionTest.java
+++
b/plugins/databases/mssql/src/test/java/org/apache/hop/databases/mssql/MsSqlServerDatabaseMeta_FieldDefinitionTest.java
@@ -37,7 +37,6 @@ class MsSqlServerDatabaseMeta_FieldDefinitionTest {
private static final String STRING_DECIMAL = "DECIMAL";
private static final String STRING_FLOAT = "FLOAT";
private static final String STRING_VARCHAR = "VARCHAR";
- private static final String STRING_TEXT = "TEXT";
@BeforeEach
void init() {
@@ -128,7 +127,7 @@ class MsSqlServerDatabaseMeta_FieldDefinitionTest {
IValueMeta valueMeta = new
MetaInterfaceBuilder(IValueMeta.TYPE_STRING).length(10_000).build();
assertEquals(
- STRING_TEXT, dbMeta.getFieldDefinition(valueMeta, null, null, false,
false, false));
+ "VARCHAR(MAX)", dbMeta.getFieldDefinition(valueMeta, null, null,
false, false, false));
}
private static class MetaInterfaceBuilder {
diff --git
a/plugins/databases/mssql/src/test/java/org/apache/hop/databases/mssql/MsSqlServerTypeRulesTest.java
b/plugins/databases/mssql/src/test/java/org/apache/hop/databases/mssql/MsSqlServerTypeRulesTest.java
new file mode 100644
index 0000000000..cb2db281bc
--- /dev/null
+++
b/plugins/databases/mssql/src/test/java/org/apache/hop/databases/mssql/MsSqlServerTypeRulesTest.java
@@ -0,0 +1,138 @@
+/*
+ * 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.databases.mssql;
+
+import static org.apache.hop.junit.database.TypeRuleFixture.column;
+import static org.apache.hop.junit.database.TypeRuleFixture.meta;
+import static org.apache.hop.junit.database.TypeRuleFixture.numericColumn;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.sql.Types;
+import org.apache.hop.core.HopClientEnvironment;
+import org.apache.hop.core.database.types.ColumnContext;
+import org.apache.hop.core.database.types.DatabaseTypeMapper;
+import org.apache.hop.core.row.IValueMeta;
+import org.apache.hop.core.row.value.ValueMetaDate;
+import org.apache.hop.core.row.value.ValueMetaString;
+import org.apache.hop.core.row.value.ValueMetaTimestamp;
+import org.apache.hop.core.variables.Variables;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+/** SQL Server write rules for date/time and Unicode string round-trips. */
+class MsSqlServerTypeRulesTest {
+
+ @BeforeAll
+ static void setUpClass() throws Exception {
+ HopClientEnvironment.init();
+ }
+
+ @Test
+ void dateColumnsStayDate() throws Exception {
+ assertEquals("DATE", write(column(Types.DATE, "date", 0, 10)));
+ }
+
+ @Test
+ void timeColumnsStayTime() throws Exception {
+ assertEquals("TIME", write(column(Types.TIME, "time", 0, 16)));
+ }
+
+ @Test
+ void datetime2IsPreserved() throws Exception {
+ IValueMeta valueMeta = new ValueMetaTimestamp("COL");
+ valueMeta.setOriginalColumnTypeName("datetime2");
+ assertEquals("DATETIME2", write(valueMeta));
+ }
+
+ @Test
+ void aPlainDateStillWritesDatetime() {
+ assertEquals("DATETIME", write(new ValueMetaDate("COL")));
+ }
+
+ @Test
+ void aPlainTimestampWritesDatetime2() {
+ assertEquals("DATETIME2", write(new ValueMetaTimestamp("COL")));
+ }
+
+ @Test
+ void aTimestampConvertedFromDateKeepsTheTime() {
+ IValueMeta valueMeta = new ValueMetaTimestamp("COL");
+ valueMeta.setOriginalColumnType(Types.DATE);
+ assertEquals("DATETIME2", write(valueMeta));
+ }
+
+ @Test
+ void nvarcharRoundTrips() throws Exception {
+ assertEquals("NVARCHAR(20)", write(column(Types.NVARCHAR, "nvarchar", 20,
20)));
+ }
+
+ @Test
+ void ncharRoundTrips() throws Exception {
+ assertEquals("NCHAR(10)", write(column(Types.NCHAR, "nchar", 10, 10)));
+ }
+
+ @Test
+ void longNationalStringsUseNvarcharMax() throws Exception {
+ assertEquals("NVARCHAR(MAX)", write(column(Types.NVARCHAR, "nvarchar",
8000, 8000)));
+ }
+
+ @Test
+ void longAnsiStringsUseVarcharMax() {
+ IValueMeta valueMeta = new ValueMetaString("COL", 10_000, 0);
+ assertEquals("VARCHAR(MAX)", write(valueMeta));
+ }
+
+ @Test
+ void aPlainStringStillWritesVarchar() {
+ IValueMeta valueMeta = new ValueMetaString("COL", 50, 0);
+ assertEquals("VARCHAR(50)", write(valueMeta));
+ }
+
+ @Test
+ void aScaledDecimalRoundTripsAsTheSameDeclaration() throws Exception {
+ assertEquals("DECIMAL(5,2)", numericRoundTrip(5, 2));
+ assertEquals("DECIMAL(24,15)", numericRoundTrip(24, 15));
+ assertEquals("DECIMAL(16,1)", numericRoundTrip(16, 1));
+ }
+
+ private static String numericRoundTrip(int precision, int scale) throws
Exception {
+ MsSqlServerDatabaseMeta dialect = new MsSqlServerDatabaseMeta();
+ IValueMeta valueMeta =
+ DatabaseTypeMapper.getValueMeta(
+ new Variables(),
+ meta(dialect),
+ numericColumn(Types.DECIMAL, "decimal", precision, scale),
+ false,
+ false);
+ return dialect.getFieldDefinition(valueMeta, null, null, false, false,
false);
+ }
+
+ private static String
write(org.apache.hop.core.database.types.DatabaseColumn column)
+ throws Exception {
+ MsSqlServerDatabaseMeta dialect = new MsSqlServerDatabaseMeta();
+ IValueMeta valueMeta =
+ DatabaseTypeMapper.getValueMeta(new Variables(), meta(dialect),
column, false, false);
+ return write(valueMeta);
+ }
+
+ private static String write(IValueMeta valueMeta) {
+ return new MsSqlServerDatabaseMeta()
+ .getColumnDefinition(
+ valueMeta, null, null, false, false, false,
ColumnContext.Purpose.CREATE);
+ }
+}
diff --git
a/plugins/databases/mssql/src/test/resources/org/apache/hop/databases/mssql/MsSqlServerFieldDefinitionGoldenTest.txt
b/plugins/databases/mssql/src/test/resources/org/apache/hop/databases/mssql/MsSqlServerFieldDefinitionGoldenTest.txt
index 3112acb5f2..93d1e112af 100644
---
a/plugins/databases/mssql/src/test/resources/org/apache/hop/databases/mssql/MsSqlServerFieldDefinitionGoldenTest.txt
+++
b/plugins/databases/mssql/src/test/resources/org/apache/hop/databases/mssql/MsSqlServerFieldDefinitionGoldenTest.txt
@@ -11,7 +11,7 @@ STRING len= 10 prec= 2 - -> VARCHAR(10)
STRING len= 15 prec= 0 - -> VARCHAR(15)
STRING len= 18 prec= 0 - -> VARCHAR(18)
STRING len= 20 prec= 0 - -> VARCHAR(20)
-STRING len= 9999999 prec= 0 - -> TEXT
+STRING len= 9999999 prec= 0 - -> VARCHAR(MAX)
INTEGER len= -1 prec= -1 - -> INT
INTEGER len= 0 prec= 0 - -> INT
INTEGER len= 4 prec= 0 - -> INT
@@ -48,15 +48,15 @@ DATE len= 15 prec= 0 - -> DATETIME
DATE len= 18 prec= 0 - -> DATETIME
DATE len= 20 prec= 0 - -> DATETIME
DATE len= 9999999 prec= 0 - -> DATETIME
-TIMESTAMP len= -1 prec= -1 - -> DATETIME
-TIMESTAMP len= 0 prec= 0 - -> DATETIME
-TIMESTAMP len= 4 prec= 0 - -> DATETIME
-TIMESTAMP len= 9 prec= 0 - -> DATETIME
-TIMESTAMP len= 10 prec= 2 - -> DATETIME
-TIMESTAMP len= 15 prec= 0 - -> DATETIME
-TIMESTAMP len= 18 prec= 0 - -> DATETIME
-TIMESTAMP len= 20 prec= 0 - -> DATETIME
-TIMESTAMP len= 9999999 prec= 0 - -> DATETIME
+TIMESTAMP len= -1 prec= -1 - -> DATETIME2
+TIMESTAMP len= 0 prec= 0 - -> DATETIME2
+TIMESTAMP len= 4 prec= 0 - -> DATETIME2
+TIMESTAMP len= 9 prec= 0 - -> DATETIME2
+TIMESTAMP len= 10 prec= 2 - -> DATETIME2
+TIMESTAMP len= 15 prec= 0 - -> DATETIME2
+TIMESTAMP len= 18 prec= 0 - -> DATETIME2
+TIMESTAMP len= 20 prec= 0 - -> DATETIME2
+TIMESTAMP len= 9999999 prec= 0 - -> DATETIME2
BOOLEAN len= -1 prec= -1 - -> CHAR(1)
BOOLEAN len= 0 prec= 0 - -> CHAR(1)
BOOLEAN len= 4 prec= 0 - -> CHAR(1)
@@ -105,34 +105,34 @@ STRING len= 3999 prec= 0 - ->
VARCHAR(3999)
STRING len= 4000 prec= 0 - -> VARCHAR(4000)
STRING len= 4001 prec= 0 - -> VARCHAR(4001)
STRING len= 7999 prec= 0 - -> VARCHAR(7999)
-STRING len= 8000 prec= 0 - -> TEXT
-STRING len= 8001 prec= 0 - -> TEXT
-STRING len= 21843 prec= 0 - -> TEXT
-STRING len= 21844 prec= 0 - -> TEXT
-STRING len= 32663 prec= 0 - -> TEXT
-STRING len= 32664 prec= 0 - -> TEXT
-STRING len= 32671 prec= 0 - -> TEXT
-STRING len= 32672 prec= 0 - -> TEXT
-STRING len= 32699 prec= 0 - -> TEXT
-STRING len= 32700 prec= 0 - -> TEXT
-STRING len= 32719 prec= 0 - -> TEXT
-STRING len= 32720 prec= 0 - -> TEXT
-STRING len= 32766 prec= 0 - -> TEXT
-STRING len= 32767 prec= 0 - -> TEXT
-STRING len= 32768 prec= 0 - -> TEXT
-STRING len= 65532 prec= 0 - -> TEXT
-STRING len= 65533 prec= 0 - -> TEXT
-STRING len= 65534 prec= 0 - -> TEXT
-STRING len= 65535 prec= 0 - -> TEXT
-STRING len= 65536 prec= 0 - -> TEXT
-STRING len= 1999999 prec= 0 - -> TEXT
-STRING len= 2000000 prec= 0 - -> TEXT
-STRING len= 2000001 prec= 0 - -> TEXT
-STRING len= 9999998 prec= 0 - -> TEXT
-STRING len= 9999999 prec= 0 - -> TEXT
-STRING len=10000000 prec= 0 - -> TEXT
-STRING len=16777215 prec= 0 - -> TEXT
-STRING len=16777216 prec= 0 - -> TEXT
+STRING len= 8000 prec= 0 - -> VARCHAR(MAX)
+STRING len= 8001 prec= 0 - -> VARCHAR(MAX)
+STRING len= 21843 prec= 0 - -> VARCHAR(MAX)
+STRING len= 21844 prec= 0 - -> VARCHAR(MAX)
+STRING len= 32663 prec= 0 - -> VARCHAR(MAX)
+STRING len= 32664 prec= 0 - -> VARCHAR(MAX)
+STRING len= 32671 prec= 0 - -> VARCHAR(MAX)
+STRING len= 32672 prec= 0 - -> VARCHAR(MAX)
+STRING len= 32699 prec= 0 - -> VARCHAR(MAX)
+STRING len= 32700 prec= 0 - -> VARCHAR(MAX)
+STRING len= 32719 prec= 0 - -> VARCHAR(MAX)
+STRING len= 32720 prec= 0 - -> VARCHAR(MAX)
+STRING len= 32766 prec= 0 - -> VARCHAR(MAX)
+STRING len= 32767 prec= 0 - -> VARCHAR(MAX)
+STRING len= 32768 prec= 0 - -> VARCHAR(MAX)
+STRING len= 65532 prec= 0 - -> VARCHAR(MAX)
+STRING len= 65533 prec= 0 - -> VARCHAR(MAX)
+STRING len= 65534 prec= 0 - -> VARCHAR(MAX)
+STRING len= 65535 prec= 0 - -> VARCHAR(MAX)
+STRING len= 65536 prec= 0 - -> VARCHAR(MAX)
+STRING len= 1999999 prec= 0 - -> VARCHAR(MAX)
+STRING len= 2000000 prec= 0 - -> VARCHAR(MAX)
+STRING len= 2000001 prec= 0 - -> VARCHAR(MAX)
+STRING len= 9999998 prec= 0 - -> VARCHAR(MAX)
+STRING len= 9999999 prec= 0 - -> VARCHAR(MAX)
+STRING len=10000000 prec= 0 - -> VARCHAR(MAX)
+STRING len=16777215 prec= 0 - -> VARCHAR(MAX)
+STRING len=16777216 prec= 0 - -> VARCHAR(MAX)
[keys]
INTEGER len= -1 prec= -1 tk -> BIGINT PRIMARY KEY
@@ -248,12 +248,12 @@ DATE len= 10 prec= 2 add -> ALTER
TABLE TBL ADD COL DATETIME
DATE len= 10 prec= 2 modify -> ALTER TABLE TBL ALTER COLUMN
COL DATETIME
DATE len= 15 prec= 0 add -> ALTER TABLE TBL ADD COL
DATETIME
DATE len= 15 prec= 0 modify -> ALTER TABLE TBL ALTER COLUMN
COL DATETIME
-TIMESTAMP len= -1 prec= -1 add -> ALTER TABLE TBL ADD COL
DATETIME
-TIMESTAMP len= -1 prec= -1 modify -> ALTER TABLE TBL ALTER COLUMN
COL DATETIME
-TIMESTAMP len= 10 prec= 2 add -> ALTER TABLE TBL ADD COL
DATETIME
-TIMESTAMP len= 10 prec= 2 modify -> ALTER TABLE TBL ALTER COLUMN
COL DATETIME
-TIMESTAMP len= 15 prec= 0 add -> ALTER TABLE TBL ADD COL
DATETIME
-TIMESTAMP len= 15 prec= 0 modify -> ALTER TABLE TBL ALTER COLUMN
COL DATETIME
+TIMESTAMP len= -1 prec= -1 add -> ALTER TABLE TBL ADD COL
DATETIME2
+TIMESTAMP len= -1 prec= -1 modify -> ALTER TABLE TBL ALTER COLUMN
COL DATETIME2
+TIMESTAMP len= 10 prec= 2 add -> ALTER TABLE TBL ADD COL
DATETIME2
+TIMESTAMP len= 10 prec= 2 modify -> ALTER TABLE TBL ALTER COLUMN
COL DATETIME2
+TIMESTAMP len= 15 prec= 0 add -> ALTER TABLE TBL ADD COL
DATETIME2
+TIMESTAMP len= 15 prec= 0 modify -> ALTER TABLE TBL ALTER COLUMN
COL DATETIME2
BOOLEAN len= -1 prec= -1 add -> ALTER TABLE TBL ADD COL CHAR(1)
BOOLEAN len= -1 prec= -1 modify -> ALTER TABLE TBL ALTER COLUMN
COL CHAR(1)
BOOLEAN len= 10 prec= 2 add -> ALTER TABLE TBL ADD COL CHAR(1)
diff --git
a/plugins/databases/mssqlnative/src/test/java/org/apache/hop/databases/mssqlnative/MsSqlServerNativeDatabaseMetaTest.java
b/plugins/databases/mssqlnative/src/test/java/org/apache/hop/databases/mssqlnative/MsSqlServerNativeDatabaseMetaTest.java
index fc7c203d43..9efac6d456 100644
---
a/plugins/databases/mssqlnative/src/test/java/org/apache/hop/databases/mssqlnative/MsSqlServerNativeDatabaseMetaTest.java
+++
b/plugins/databases/mssqlnative/src/test/java/org/apache/hop/databases/mssqlnative/MsSqlServerNativeDatabaseMetaTest.java
@@ -19,9 +19,16 @@ package org.apache.hop.databases.mssqlnative;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import java.sql.Types;
+import org.apache.hop.core.HopClientEnvironment;
import org.apache.hop.core.database.DatabaseMeta;
import org.apache.hop.core.database.IDatabase;
+import org.apache.hop.core.database.types.ColumnContext;
+import org.apache.hop.core.database.types.DatabaseTypeMapper;
+import org.apache.hop.core.row.IValueMeta;
+import org.apache.hop.core.variables.Variables;
import org.apache.hop.databases.mssql.MsSqlServerDatabaseMeta;
+import org.apache.hop.junit.database.TypeRuleFixture;
import org.apache.hop.junit.rules.RestoreHopEngineEnvironmentExtension;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
@@ -30,6 +37,23 @@ class MsSqlServerNativeDatabaseMetaTest {
@RegisterExtension
static RestoreHopEngineEnvironmentExtension env = new
RestoreHopEngineEnvironmentExtension();
+ @Test
+ void nvarcharRoundTripUsesTheInheritedWriteRules() throws Exception {
+ HopClientEnvironment.init();
+ MsSqlServerNativeDatabaseMeta nativeMeta = new
MsSqlServerNativeDatabaseMeta();
+ IValueMeta valueMeta =
+ DatabaseTypeMapper.getValueMeta(
+ new Variables(),
+ TypeRuleFixture.meta(nativeMeta),
+ TypeRuleFixture.column(Types.NVARCHAR, "nvarchar", 20, 20),
+ false,
+ false);
+ assertEquals(
+ "NVARCHAR(20)",
+ nativeMeta.getColumnDefinition(
+ valueMeta, null, null, false, false, false,
ColumnContext.Purpose.CREATE));
+ }
+
@Test
void testMsSqlOverrides() {
MsSqlServerNativeDatabaseMeta localNativeMeta = new
MsSqlServerNativeDatabaseMeta();
diff --git
a/plugins/databases/mssqlnative/src/test/resources/org/apache/hop/databases/mssqlnative/MsSqlServerNativeFieldDefinitionGoldenTest.txt
b/plugins/databases/mssqlnative/src/test/resources/org/apache/hop/databases/mssqlnative/MsSqlServerNativeFieldDefinitionGoldenTest.txt
index ea82087cda..271ebb2ba5 100644
---
a/plugins/databases/mssqlnative/src/test/resources/org/apache/hop/databases/mssqlnative/MsSqlServerNativeFieldDefinitionGoldenTest.txt
+++
b/plugins/databases/mssqlnative/src/test/resources/org/apache/hop/databases/mssqlnative/MsSqlServerNativeFieldDefinitionGoldenTest.txt
@@ -11,7 +11,7 @@ STRING len= 10 prec= 2 - -> VARCHAR(10)
STRING len= 15 prec= 0 - -> VARCHAR(15)
STRING len= 18 prec= 0 - -> VARCHAR(18)
STRING len= 20 prec= 0 - -> VARCHAR(20)
-STRING len= 9999999 prec= 0 - -> TEXT
+STRING len= 9999999 prec= 0 - -> VARCHAR(MAX)
INTEGER len= -1 prec= -1 - -> INT
INTEGER len= 0 prec= 0 - -> INT
INTEGER len= 4 prec= 0 - -> INT
@@ -48,15 +48,15 @@ DATE len= 15 prec= 0 - -> DATETIME
DATE len= 18 prec= 0 - -> DATETIME
DATE len= 20 prec= 0 - -> DATETIME
DATE len= 9999999 prec= 0 - -> DATETIME
-TIMESTAMP len= -1 prec= -1 - -> DATETIME
-TIMESTAMP len= 0 prec= 0 - -> DATETIME
-TIMESTAMP len= 4 prec= 0 - -> DATETIME
-TIMESTAMP len= 9 prec= 0 - -> DATETIME
-TIMESTAMP len= 10 prec= 2 - -> DATETIME
-TIMESTAMP len= 15 prec= 0 - -> DATETIME
-TIMESTAMP len= 18 prec= 0 - -> DATETIME
-TIMESTAMP len= 20 prec= 0 - -> DATETIME
-TIMESTAMP len= 9999999 prec= 0 - -> DATETIME
+TIMESTAMP len= -1 prec= -1 - -> DATETIME2
+TIMESTAMP len= 0 prec= 0 - -> DATETIME2
+TIMESTAMP len= 4 prec= 0 - -> DATETIME2
+TIMESTAMP len= 9 prec= 0 - -> DATETIME2
+TIMESTAMP len= 10 prec= 2 - -> DATETIME2
+TIMESTAMP len= 15 prec= 0 - -> DATETIME2
+TIMESTAMP len= 18 prec= 0 - -> DATETIME2
+TIMESTAMP len= 20 prec= 0 - -> DATETIME2
+TIMESTAMP len= 9999999 prec= 0 - -> DATETIME2
BOOLEAN len= -1 prec= -1 - -> CHAR(1)
BOOLEAN len= 0 prec= 0 - -> CHAR(1)
BOOLEAN len= 4 prec= 0 - -> CHAR(1)
@@ -105,34 +105,34 @@ STRING len= 3999 prec= 0 - ->
VARCHAR(3999)
STRING len= 4000 prec= 0 - -> VARCHAR(4000)
STRING len= 4001 prec= 0 - -> VARCHAR(4001)
STRING len= 7999 prec= 0 - -> VARCHAR(7999)
-STRING len= 8000 prec= 0 - -> TEXT
-STRING len= 8001 prec= 0 - -> TEXT
-STRING len= 21843 prec= 0 - -> TEXT
-STRING len= 21844 prec= 0 - -> TEXT
-STRING len= 32663 prec= 0 - -> TEXT
-STRING len= 32664 prec= 0 - -> TEXT
-STRING len= 32671 prec= 0 - -> TEXT
-STRING len= 32672 prec= 0 - -> TEXT
-STRING len= 32699 prec= 0 - -> TEXT
-STRING len= 32700 prec= 0 - -> TEXT
-STRING len= 32719 prec= 0 - -> TEXT
-STRING len= 32720 prec= 0 - -> TEXT
-STRING len= 32766 prec= 0 - -> TEXT
-STRING len= 32767 prec= 0 - -> TEXT
-STRING len= 32768 prec= 0 - -> TEXT
-STRING len= 65532 prec= 0 - -> TEXT
-STRING len= 65533 prec= 0 - -> TEXT
-STRING len= 65534 prec= 0 - -> TEXT
-STRING len= 65535 prec= 0 - -> TEXT
-STRING len= 65536 prec= 0 - -> TEXT
-STRING len= 1999999 prec= 0 - -> TEXT
-STRING len= 2000000 prec= 0 - -> TEXT
-STRING len= 2000001 prec= 0 - -> TEXT
-STRING len= 9999998 prec= 0 - -> TEXT
-STRING len= 9999999 prec= 0 - -> TEXT
-STRING len=10000000 prec= 0 - -> TEXT
-STRING len=16777215 prec= 0 - -> TEXT
-STRING len=16777216 prec= 0 - -> TEXT
+STRING len= 8000 prec= 0 - -> VARCHAR(MAX)
+STRING len= 8001 prec= 0 - -> VARCHAR(MAX)
+STRING len= 21843 prec= 0 - -> VARCHAR(MAX)
+STRING len= 21844 prec= 0 - -> VARCHAR(MAX)
+STRING len= 32663 prec= 0 - -> VARCHAR(MAX)
+STRING len= 32664 prec= 0 - -> VARCHAR(MAX)
+STRING len= 32671 prec= 0 - -> VARCHAR(MAX)
+STRING len= 32672 prec= 0 - -> VARCHAR(MAX)
+STRING len= 32699 prec= 0 - -> VARCHAR(MAX)
+STRING len= 32700 prec= 0 - -> VARCHAR(MAX)
+STRING len= 32719 prec= 0 - -> VARCHAR(MAX)
+STRING len= 32720 prec= 0 - -> VARCHAR(MAX)
+STRING len= 32766 prec= 0 - -> VARCHAR(MAX)
+STRING len= 32767 prec= 0 - -> VARCHAR(MAX)
+STRING len= 32768 prec= 0 - -> VARCHAR(MAX)
+STRING len= 65532 prec= 0 - -> VARCHAR(MAX)
+STRING len= 65533 prec= 0 - -> VARCHAR(MAX)
+STRING len= 65534 prec= 0 - -> VARCHAR(MAX)
+STRING len= 65535 prec= 0 - -> VARCHAR(MAX)
+STRING len= 65536 prec= 0 - -> VARCHAR(MAX)
+STRING len= 1999999 prec= 0 - -> VARCHAR(MAX)
+STRING len= 2000000 prec= 0 - -> VARCHAR(MAX)
+STRING len= 2000001 prec= 0 - -> VARCHAR(MAX)
+STRING len= 9999998 prec= 0 - -> VARCHAR(MAX)
+STRING len= 9999999 prec= 0 - -> VARCHAR(MAX)
+STRING len=10000000 prec= 0 - -> VARCHAR(MAX)
+STRING len=16777215 prec= 0 - -> VARCHAR(MAX)
+STRING len=16777216 prec= 0 - -> VARCHAR(MAX)
[keys]
INTEGER len= -1 prec= -1 tk -> BIGINT PRIMARY KEY
@@ -248,12 +248,12 @@ DATE len= 10 prec= 2 add -> ALTER
TABLE TBL ADD COL DATETIME
DATE len= 10 prec= 2 modify -> ALTER TABLE TBL ALTER COLUMN
COL DATETIME
DATE len= 15 prec= 0 add -> ALTER TABLE TBL ADD COL
DATETIME
DATE len= 15 prec= 0 modify -> ALTER TABLE TBL ALTER COLUMN
COL DATETIME
-TIMESTAMP len= -1 prec= -1 add -> ALTER TABLE TBL ADD COL
DATETIME
-TIMESTAMP len= -1 prec= -1 modify -> ALTER TABLE TBL ALTER COLUMN
COL DATETIME
-TIMESTAMP len= 10 prec= 2 add -> ALTER TABLE TBL ADD COL
DATETIME
-TIMESTAMP len= 10 prec= 2 modify -> ALTER TABLE TBL ALTER COLUMN
COL DATETIME
-TIMESTAMP len= 15 prec= 0 add -> ALTER TABLE TBL ADD COL
DATETIME
-TIMESTAMP len= 15 prec= 0 modify -> ALTER TABLE TBL ALTER COLUMN
COL DATETIME
+TIMESTAMP len= -1 prec= -1 add -> ALTER TABLE TBL ADD COL
DATETIME2
+TIMESTAMP len= -1 prec= -1 modify -> ALTER TABLE TBL ALTER COLUMN
COL DATETIME2
+TIMESTAMP len= 10 prec= 2 add -> ALTER TABLE TBL ADD COL
DATETIME2
+TIMESTAMP len= 10 prec= 2 modify -> ALTER TABLE TBL ALTER COLUMN
COL DATETIME2
+TIMESTAMP len= 15 prec= 0 add -> ALTER TABLE TBL ADD COL
DATETIME2
+TIMESTAMP len= 15 prec= 0 modify -> ALTER TABLE TBL ALTER COLUMN
COL DATETIME2
BOOLEAN len= -1 prec= -1 add -> ALTER TABLE TBL ADD COL CHAR(1)
BOOLEAN len= -1 prec= -1 modify -> ALTER TABLE TBL ALTER COLUMN
COL CHAR(1)
BOOLEAN len= 10 prec= 2 add -> ALTER TABLE TBL ADD COL CHAR(1)
diff --git
a/plugins/databases/mysql/src/test/java/org/apache/hop/databases/mysql/MySqlTypeRulesTest.java
b/plugins/databases/mysql/src/test/java/org/apache/hop/databases/mysql/MySqlTypeRulesTest.java
index ba78e10c5b..b63cc26279 100644
---
a/plugins/databases/mysql/src/test/java/org/apache/hop/databases/mysql/MySqlTypeRulesTest.java
+++
b/plugins/databases/mysql/src/test/java/org/apache/hop/databases/mysql/MySqlTypeRulesTest.java
@@ -19,6 +19,7 @@ package org.apache.hop.databases.mysql;
import static org.apache.hop.junit.database.TypeRuleFixture.column;
import static org.apache.hop.junit.database.TypeRuleFixture.meta;
+import static org.apache.hop.junit.database.TypeRuleFixture.numericColumn;
import static org.apache.hop.junit.database.TypeRuleFixture.properties;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -92,4 +93,21 @@ class MySqlTypeRulesTest {
assertTrue(valueMeta.isBinary());
assertEquals(8, valueMeta.getLength());
}
+
+ @Test
+ void aWideExactDecimalRoundTripsAsDecimalNotDouble() throws Exception {
+ MySqlDatabaseMeta dialect = new MySqlDatabaseMeta();
+ IValueMeta valueMeta =
+ DatabaseTypeMapper.getValueMeta(
+ new Variables(),
+ meta(dialect),
+ numericColumn(Types.DECIMAL, "decimal", 24, 15),
+ false,
+ false);
+ assertTrue(valueMeta.isBigNumber() || valueMeta.isNumber());
+ assertEquals(24, valueMeta.getLength());
+ assertEquals(15, valueMeta.getPrecision());
+ assertEquals(
+ "DECIMAL(24, 15)", dialect.getFieldDefinition(valueMeta, null, null,
false, false, false));
+ }
}
diff --git
a/plugins/databases/postgresql/src/main/java/org/apache/hop/databases/postgresql/PostgreSqlDatabaseMeta.java
b/plugins/databases/postgresql/src/main/java/org/apache/hop/databases/postgresql/PostgreSqlDatabaseMeta.java
index 539dd41e24..1bb119c35f 100644
---
a/plugins/databases/postgresql/src/main/java/org/apache/hop/databases/postgresql/PostgreSqlDatabaseMeta.java
+++
b/plugins/databases/postgresql/src/main/java/org/apache/hop/databases/postgresql/PostgreSqlDatabaseMeta.java
@@ -484,8 +484,10 @@ public class PostgreSqlDatabaseMeta extends
BaseDatabaseMeta implements IDatabas
} else {
if (length > 0) {
if (precision > 0 || length > 18) {
- // Numeric(Precision, Scale): Precision = total length; Scale =
decimal places
- int numericPrecision = length + precision;
+ // Numeric(p, s): Hop length is the total number of significant
digits. PostgreSQL
+ // before 15 rejects s > p, so widen p when a field was authored
with scale larger
+ // than length.
+ int numericPrecision = Math.max(length, precision);
if (numericPrecision > MAX_NUMERIC_PRECISION) {
// PostgreSQL refuses a declared precision above 1000
outright: "NUMERIC precision
// 1073741824 must be between 1 and 1000". A length that large
only ever arrives
diff --git
a/plugins/databases/postgresql/src/test/java/org/apache/hop/databases/postgresql/PostgreSqlDatabaseMetaTest.java
b/plugins/databases/postgresql/src/test/java/org/apache/hop/databases/postgresql/PostgreSqlDatabaseMetaTest.java
index 84c48b79d6..bafdbec968 100644
---
a/plugins/databases/postgresql/src/test/java/org/apache/hop/databases/postgresql/PostgreSqlDatabaseMetaTest.java
+++
b/plugins/databases/postgresql/src/test/java/org/apache/hop/databases/postgresql/PostgreSqlDatabaseMetaTest.java
@@ -764,17 +764,17 @@ public class PostgreSqlDatabaseMetaTest {
"FOO", new ValueMetaNumber("BAR", 5, 0), "", false, "", false));
assertEquals(
- "ALTER TABLE FOO ADD COLUMN BAR NUMERIC(13, 3)",
+ "ALTER TABLE FOO ADD COLUMN BAR NUMERIC(10, 3)",
nativeMeta.getAddColumnStatement(
"FOO", new ValueMetaNumber("BAR", 10, 3), "", false, "", false));
assertEquals(
- "ALTER TABLE FOO ADD COLUMN BAR NUMERIC(13, 3)",
+ "ALTER TABLE FOO ADD COLUMN BAR NUMERIC(10, 3)",
nativeMeta.getAddColumnStatement(
"FOO", new ValueMetaBigNumber("BAR", 10, 3), "", false, "",
false));
assertEquals(
- "ALTER TABLE FOO ADD COLUMN BAR NUMERIC(25, 4)",
+ "ALTER TABLE FOO ADD COLUMN BAR NUMERIC(21, 4)",
nativeMeta.getAddColumnStatement(
"FOO", new ValueMetaBigNumber("BAR", 21, 4), "", false, "",
false));
@@ -804,7 +804,7 @@ public class PostgreSqlDatabaseMetaTest {
false)); // Bug here - invalid SQL
assertEquals(
- "ALTER TABLE FOO ADD COLUMN BAR NUMERIC(29, 7)",
+ "ALTER TABLE FOO ADD COLUMN BAR NUMERIC(22, 7)",
nativeMeta.getAddColumnStatement(
"FOO", new ValueMetaBigNumber("BAR", 22, 7), "", false, "",
false));
assertEquals(
@@ -812,7 +812,7 @@ public class PostgreSqlDatabaseMetaTest {
nativeMeta.getAddColumnStatement(
"FOO", new ValueMetaNumber("BAR", -10, 7), "", false, "", false));
assertEquals(
- "ALTER TABLE FOO ADD COLUMN BAR NUMERIC(12, 7)",
+ "ALTER TABLE FOO ADD COLUMN BAR NUMERIC(7, 7)",
nativeMeta.getAddColumnStatement(
"FOO", new ValueMetaNumber("BAR", 5, 7), "", false, "", false));
// An ALTER TABLE spells a column the way a CREATE TABLE does: through the
dialect's type
diff --git
a/plugins/databases/postgresql/src/test/java/org/apache/hop/databases/postgresql/PostgreSqlTypeRulesTest.java
b/plugins/databases/postgresql/src/test/java/org/apache/hop/databases/postgresql/PostgreSqlTypeRulesTest.java
index 1e0b197bdd..abcff310bf 100644
---
a/plugins/databases/postgresql/src/test/java/org/apache/hop/databases/postgresql/PostgreSqlTypeRulesTest.java
+++
b/plugins/databases/postgresql/src/test/java/org/apache/hop/databases/postgresql/PostgreSqlTypeRulesTest.java
@@ -71,11 +71,25 @@ class PostgreSqlTypeRulesTest {
IValueMeta valueMeta = map(Types.NUMERIC, "numeric", 10, 2);
assertTrue(valueMeta.isNumber());
- // Hop length is the digits before the decimal, so it round-trips as
NUMERIC(8+2, 2).
- assertEquals(8, valueMeta.getLength());
+ // Hop length is the total number of significant digits, the same as the
database precision.
+ assertEquals(10, valueMeta.getLength());
assertEquals(2, valueMeta.getPrecision());
}
+ @Test
+ void aScaledNumericRoundTripsAsTheSameDeclaration() throws Exception {
+ assertEquals("NUMERIC(5, 2)", roundTrip(Types.NUMERIC, "numeric", 5, 2));
+ assertEquals("NUMERIC(24, 15)", roundTrip(Types.DECIMAL, "numeric", 24,
15));
+ assertEquals("NUMERIC(16, 1)", roundTrip(Types.NUMERIC, "numeric", 16, 1));
+ }
+
+ private String roundTrip(int sqlType, String typeName, int precision, int
scale)
+ throws Exception {
+ PostgreSqlDatabaseMeta dialect = new PostgreSqlDatabaseMeta();
+ IValueMeta valueMeta = map(sqlType, typeName, precision, scale);
+ return dialect.getFieldDefinition(valueMeta, null, null, false, false,
false);
+ }
+
/**
* An address is Types.OTHER, which the standard mapping takes as a string.
The dialect has to
* name it, or the value type Hop has for addresses never sees the column.
diff --git
a/plugins/databases/postgresql/src/test/resources/org/apache/hop/databases/postgresql/PostgreSqlFieldDefinitionGoldenTest.txt
b/plugins/databases/postgresql/src/test/resources/org/apache/hop/databases/postgresql/PostgreSqlFieldDefinitionGoldenTest.txt
index 679a316226..d053b4739d 100644
---
a/plugins/databases/postgresql/src/test/resources/org/apache/hop/databases/postgresql/PostgreSqlFieldDefinitionGoldenTest.txt
+++
b/plugins/databases/postgresql/src/test/resources/org/apache/hop/databases/postgresql/PostgreSqlFieldDefinitionGoldenTest.txt
@@ -25,7 +25,7 @@ NUMBER len= -1 prec= -1 - -> DOUBLE
PRECISION
NUMBER len= 0 prec= 0 - -> DOUBLE PRECISION
NUMBER len= 4 prec= 0 - -> SMALLINT
NUMBER len= 9 prec= 0 - -> INTEGER
-NUMBER len= 10 prec= 2 - -> NUMERIC(12, 2)
+NUMBER len= 10 prec= 2 - -> NUMERIC(10, 2)
NUMBER len= 15 prec= 0 - -> BIGINT
NUMBER len= 18 prec= 0 - -> BIGINT
NUMBER len= 20 prec= 0 - -> NUMERIC(20, 0)
@@ -34,7 +34,7 @@ BIGNUMBER len= -1 prec= -1 - -> DOUBLE
PRECISION
BIGNUMBER len= 0 prec= 0 - -> DOUBLE PRECISION
BIGNUMBER len= 4 prec= 0 - -> SMALLINT
BIGNUMBER len= 9 prec= 0 - -> INTEGER
-BIGNUMBER len= 10 prec= 2 - -> NUMERIC(12, 2)
+BIGNUMBER len= 10 prec= 2 - -> NUMERIC(10, 2)
BIGNUMBER len= 15 prec= 0 - -> BIGINT
BIGNUMBER len= 18 prec= 0 - -> BIGINT
BIGNUMBER len= 20 prec= 0 - -> NUMERIC(20, 0)
@@ -232,14 +232,14 @@ INTEGER len= 15 prec= 0 add -> ALTER
TABLE TBL ADD COLUMN COL B
INTEGER len= 15 prec= 0 modify -> ALTER TABLE TBL ADD COLUMN
COL_KTL BIGINT; UPDATE TBL SET COL_KTL=COL; ALTER TABLE TBL DROP COLUMN COL;
ALTER TABLE TBL RENAME COL_KTL TO COL;
NUMBER len= -1 prec= -1 add -> ALTER TABLE TBL ADD COLUMN COL
DOUBLE PRECISION
NUMBER len= -1 prec= -1 modify -> ALTER TABLE TBL ADD COLUMN
COL_KTL DOUBLE PRECISION; UPDATE TBL SET COL_KTL=COL; ALTER TABLE TBL DROP
COLUMN COL; ALTER TABLE TBL RENAME COL_KTL TO COL;
-NUMBER len= 10 prec= 2 add -> ALTER TABLE TBL ADD COLUMN COL
NUMERIC(12, 2)
-NUMBER len= 10 prec= 2 modify -> ALTER TABLE TBL ADD COLUMN
COL_KTL NUMERIC(12, 2); UPDATE TBL SET COL_KTL=COL; ALTER TABLE TBL DROP COLUMN
COL; ALTER TABLE TBL RENAME COL_KTL TO COL;
+NUMBER len= 10 prec= 2 add -> ALTER TABLE TBL ADD COLUMN COL
NUMERIC(10, 2)
+NUMBER len= 10 prec= 2 modify -> ALTER TABLE TBL ADD COLUMN
COL_KTL NUMERIC(10, 2); UPDATE TBL SET COL_KTL=COL; ALTER TABLE TBL DROP COLUMN
COL; ALTER TABLE TBL RENAME COL_KTL TO COL;
NUMBER len= 15 prec= 0 add -> ALTER TABLE TBL ADD COLUMN COL
BIGINT
NUMBER len= 15 prec= 0 modify -> ALTER TABLE TBL ADD COLUMN
COL_KTL BIGINT; UPDATE TBL SET COL_KTL=COL; ALTER TABLE TBL DROP COLUMN COL;
ALTER TABLE TBL RENAME COL_KTL TO COL;
BIGNUMBER len= -1 prec= -1 add -> ALTER TABLE TBL ADD COLUMN COL
DOUBLE PRECISION
BIGNUMBER len= -1 prec= -1 modify -> ALTER TABLE TBL ADD COLUMN
COL_KTL DOUBLE PRECISION; UPDATE TBL SET COL_KTL=COL; ALTER TABLE TBL DROP
COLUMN COL; ALTER TABLE TBL RENAME COL_KTL TO COL;
-BIGNUMBER len= 10 prec= 2 add -> ALTER TABLE TBL ADD COLUMN COL
NUMERIC(12, 2)
-BIGNUMBER len= 10 prec= 2 modify -> ALTER TABLE TBL ADD COLUMN
COL_KTL NUMERIC(12, 2); UPDATE TBL SET COL_KTL=COL; ALTER TABLE TBL DROP COLUMN
COL; ALTER TABLE TBL RENAME COL_KTL TO COL;
+BIGNUMBER len= 10 prec= 2 add -> ALTER TABLE TBL ADD COLUMN COL
NUMERIC(10, 2)
+BIGNUMBER len= 10 prec= 2 modify -> ALTER TABLE TBL ADD COLUMN
COL_KTL NUMERIC(10, 2); UPDATE TBL SET COL_KTL=COL; ALTER TABLE TBL DROP COLUMN
COL; ALTER TABLE TBL RENAME COL_KTL TO COL;
BIGNUMBER len= 15 prec= 0 add -> ALTER TABLE TBL ADD COLUMN COL
BIGINT
BIGNUMBER len= 15 prec= 0 modify -> ALTER TABLE TBL ADD COLUMN
COL_KTL BIGINT; UPDATE TBL SET COL_KTL=COL; ALTER TABLE TBL DROP COLUMN COL;
ALTER TABLE TBL RENAME COL_KTL TO COL;
DATE len= -1 prec= -1 add -> ALTER TABLE TBL ADD COLUMN COL
TIMESTAMP
diff --git
a/plugins/databases/redshift/src/test/resources/org/apache/hop/databases/redshift/RedshiftFieldDefinitionGoldenTest.txt
b/plugins/databases/redshift/src/test/resources/org/apache/hop/databases/redshift/RedshiftFieldDefinitionGoldenTest.txt
index 5407684bad..cd569d0e1f 100644
---
a/plugins/databases/redshift/src/test/resources/org/apache/hop/databases/redshift/RedshiftFieldDefinitionGoldenTest.txt
+++
b/plugins/databases/redshift/src/test/resources/org/apache/hop/databases/redshift/RedshiftFieldDefinitionGoldenTest.txt
@@ -25,7 +25,7 @@ NUMBER len= -1 prec= -1 - -> DOUBLE
PRECISION
NUMBER len= 0 prec= 0 - -> DOUBLE PRECISION
NUMBER len= 4 prec= 0 - -> SMALLINT
NUMBER len= 9 prec= 0 - -> INTEGER
-NUMBER len= 10 prec= 2 - -> NUMERIC(12, 2)
+NUMBER len= 10 prec= 2 - -> NUMERIC(10, 2)
NUMBER len= 15 prec= 0 - -> BIGINT
NUMBER len= 18 prec= 0 - -> BIGINT
NUMBER len= 20 prec= 0 - -> NUMERIC(20, 0)
@@ -34,7 +34,7 @@ BIGNUMBER len= -1 prec= -1 - -> DOUBLE
PRECISION
BIGNUMBER len= 0 prec= 0 - -> DOUBLE PRECISION
BIGNUMBER len= 4 prec= 0 - -> SMALLINT
BIGNUMBER len= 9 prec= 0 - -> INTEGER
-BIGNUMBER len= 10 prec= 2 - -> NUMERIC(12, 2)
+BIGNUMBER len= 10 prec= 2 - -> NUMERIC(10, 2)
BIGNUMBER len= 15 prec= 0 - -> BIGINT
BIGNUMBER len= 18 prec= 0 - -> BIGINT
BIGNUMBER len= 20 prec= 0 - -> NUMERIC(20, 0)
@@ -232,14 +232,14 @@ INTEGER len= 15 prec= 0 add -> ALTER
TABLE TBL ADD COLUMN COL B
INTEGER len= 15 prec= 0 modify -> ALTER TABLE TBL ADD COLUMN
COL_KTL BIGINT; UPDATE TBL SET COL_KTL=COL; ALTER TABLE TBL DROP COLUMN COL;
ALTER TABLE TBL RENAME COL_KTL TO COL;
NUMBER len= -1 prec= -1 add -> ALTER TABLE TBL ADD COLUMN COL
DOUBLE PRECISION
NUMBER len= -1 prec= -1 modify -> ALTER TABLE TBL ADD COLUMN
COL_KTL DOUBLE PRECISION; UPDATE TBL SET COL_KTL=COL; ALTER TABLE TBL DROP
COLUMN COL; ALTER TABLE TBL RENAME COL_KTL TO COL;
-NUMBER len= 10 prec= 2 add -> ALTER TABLE TBL ADD COLUMN COL
NUMERIC(12, 2)
-NUMBER len= 10 prec= 2 modify -> ALTER TABLE TBL ADD COLUMN
COL_KTL NUMERIC(12, 2); UPDATE TBL SET COL_KTL=COL; ALTER TABLE TBL DROP COLUMN
COL; ALTER TABLE TBL RENAME COL_KTL TO COL;
+NUMBER len= 10 prec= 2 add -> ALTER TABLE TBL ADD COLUMN COL
NUMERIC(10, 2)
+NUMBER len= 10 prec= 2 modify -> ALTER TABLE TBL ADD COLUMN
COL_KTL NUMERIC(10, 2); UPDATE TBL SET COL_KTL=COL; ALTER TABLE TBL DROP COLUMN
COL; ALTER TABLE TBL RENAME COL_KTL TO COL;
NUMBER len= 15 prec= 0 add -> ALTER TABLE TBL ADD COLUMN COL
BIGINT
NUMBER len= 15 prec= 0 modify -> ALTER TABLE TBL ADD COLUMN
COL_KTL BIGINT; UPDATE TBL SET COL_KTL=COL; ALTER TABLE TBL DROP COLUMN COL;
ALTER TABLE TBL RENAME COL_KTL TO COL;
BIGNUMBER len= -1 prec= -1 add -> ALTER TABLE TBL ADD COLUMN COL
DOUBLE PRECISION
BIGNUMBER len= -1 prec= -1 modify -> ALTER TABLE TBL ADD COLUMN
COL_KTL DOUBLE PRECISION; UPDATE TBL SET COL_KTL=COL; ALTER TABLE TBL DROP
COLUMN COL; ALTER TABLE TBL RENAME COL_KTL TO COL;
-BIGNUMBER len= 10 prec= 2 add -> ALTER TABLE TBL ADD COLUMN COL
NUMERIC(12, 2)
-BIGNUMBER len= 10 prec= 2 modify -> ALTER TABLE TBL ADD COLUMN
COL_KTL NUMERIC(12, 2); UPDATE TBL SET COL_KTL=COL; ALTER TABLE TBL DROP COLUMN
COL; ALTER TABLE TBL RENAME COL_KTL TO COL;
+BIGNUMBER len= 10 prec= 2 add -> ALTER TABLE TBL ADD COLUMN COL
NUMERIC(10, 2)
+BIGNUMBER len= 10 prec= 2 modify -> ALTER TABLE TBL ADD COLUMN
COL_KTL NUMERIC(10, 2); UPDATE TBL SET COL_KTL=COL; ALTER TABLE TBL DROP COLUMN
COL; ALTER TABLE TBL RENAME COL_KTL TO COL;
BIGNUMBER len= 15 prec= 0 add -> ALTER TABLE TBL ADD COLUMN COL
BIGINT
BIGNUMBER len= 15 prec= 0 modify -> ALTER TABLE TBL ADD COLUMN
COL_KTL BIGINT; UPDATE TBL SET COL_KTL=COL; ALTER TABLE TBL DROP COLUMN COL;
ALTER TABLE TBL RENAME COL_KTL TO COL;
DATE len= -1 prec= -1 add -> ALTER TABLE TBL ADD COLUMN COL
TIMESTAMP
diff --git
a/plugins/transforms/streamlookup/src/main/java/org/apache/hop/pipeline/transforms/streamlookup/StreamLookup.java
b/plugins/transforms/streamlookup/src/main/java/org/apache/hop/pipeline/transforms/streamlookup/StreamLookup.java
index 16208e81ca..748b5e90ba 100644
---
a/plugins/transforms/streamlookup/src/main/java/org/apache/hop/pipeline/transforms/streamlookup/StreamLookup.java
+++
b/plugins/transforms/streamlookup/src/main/java/org/apache/hop/pipeline/transforms/streamlookup/StreamLookup.java
@@ -74,7 +74,7 @@ public class StreamLookup extends
BaseTransform<StreamLookupMeta, StreamLookupDa
data.nullIf[i] = data.valueDefault[i];
}
break;
- case IValueMeta.TYPE_DATE:
+ case IValueMeta.TYPE_DATE, IValueMeta.TYPE_TIMESTAMP:
try {
data.nullIf[i] =
DateFormat.getInstance().parse(data.valueDefault[i]);
} catch (Exception e) {
diff --git
a/plugins/transforms/streamlookup/src/test/java/org/apache/hop/pipeline/transforms/streamlookup/StreamLookupTest.java
b/plugins/transforms/streamlookup/src/test/java/org/apache/hop/pipeline/transforms/streamlookup/StreamLookupTest.java
new file mode 100644
index 0000000000..efaf8c4459
--- /dev/null
+++
b/plugins/transforms/streamlookup/src/test/java/org/apache/hop/pipeline/transforms/streamlookup/StreamLookupTest.java
@@ -0,0 +1,81 @@
+/*
+ * 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.streamlookup;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.when;
+
+import java.lang.reflect.Method;
+import java.text.DateFormat;
+import java.util.Date;
+import org.apache.hop.core.logging.ILoggingObject;
+import org.apache.hop.core.row.IValueMeta;
+import org.apache.hop.core.row.value.ValueMetaPluginType;
+import org.apache.hop.junit.rules.RestoreHopEnvironmentExtension;
+import org.apache.hop.pipeline.transforms.mock.TransformMockHelper;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+@ExtendWith(RestoreHopEnvironmentExtension.class)
+class StreamLookupTest {
+
+ private TransformMockHelper<StreamLookupMeta, StreamLookupData> mockHelper;
+
+ @BeforeEach
+ void setUp() throws Exception {
+ ValueMetaPluginType.getInstance().searchPlugins();
+ mockHelper =
+ new TransformMockHelper<>("lookup", StreamLookupMeta.class,
StreamLookupData.class);
+ when(mockHelper.logChannelFactory.create(any(), any(ILoggingObject.class)))
+ .thenReturn(mockHelper.iLogChannel);
+
when(mockHelper.logChannelFactory.create(any())).thenReturn(mockHelper.iLogChannel);
+ }
+
+ @AfterEach
+ void tearDown() {
+ mockHelper.cleanUp();
+ }
+
+ @Test
+ void timestampDefaultValueDoesNotThrowConversionNotImplemented() throws
Exception {
+ StreamLookupMeta meta = new StreamLookupMeta();
+ StreamLookupMeta.ReturnValue returnValue = new
StreamLookupMeta.ReturnValue();
+ returnValue.setValue("ts");
+ returnValue.setValueName("ts");
+ returnValue.setValueDefault(DateFormat.getInstance().format(new Date()));
+ returnValue.setValueDefaultType(IValueMeta.TYPE_TIMESTAMP);
+ meta.getLookup().getReturnValues().add(returnValue);
+
+ StreamLookupData data = new StreamLookupData();
+ data.valueDefault = new String[] {returnValue.getValueDefault()};
+
+ StreamLookup transform =
+ new StreamLookup(
+ mockHelper.transformMeta, meta, data, 0, mockHelper.pipelineMeta,
mockHelper.pipeline);
+
+ Method handleNullIf = StreamLookup.class.getDeclaredMethod("handleNullIf");
+ handleNullIf.setAccessible(true);
+ assertDoesNotThrow(() -> handleNullIf.invoke(transform));
+ assertNotNull(data.nullIf);
+ assertNotNull(data.nullIf[0]);
+ }
+}
diff --git a/ui/src/main/java/org/apache/hop/ui/core/PropsUi.java
b/ui/src/main/java/org/apache/hop/ui/core/PropsUi.java
index 8202d76abb..94eb314fb5 100644
--- a/ui/src/main/java/org/apache/hop/ui/core/PropsUi.java
+++ b/ui/src/main/java/org/apache/hop/ui/core/PropsUi.java
@@ -94,7 +94,6 @@ public class PropsUi extends Props {
private static final String DISABLE_ZOOM_SCROLLING = "DisableZoomScrolling";
private static final String METRICS_ABOVE_SELECTED_TRANSFORMS =
"MetricsAboveSelectedTransforms";
private static final String ENABLE_INFINITE_CANVAS_MOVE =
"EnableInfiniteCanvasMove";
- private static final String USE_ADVANCED_TERMINAL = "UseAdvancedTerminal";
private static final String REMEMBER_DIALOG_POSITIONS =
"RememberDialogPositions";
private static final String RESET_DIALOG_POSITIONS_ON_RESTART =
"ResetDialogPositionsOnRestart";
diff --git
a/ui/src/main/java/org/apache/hop/ui/core/dialog/ProgressMonitorDialog.java
b/ui/src/main/java/org/apache/hop/ui/core/dialog/ProgressMonitorDialog.java
index 2fb4bb054a..d39d360925 100644
--- a/ui/src/main/java/org/apache/hop/ui/core/dialog/ProgressMonitorDialog.java
+++ b/ui/src/main/java/org/apache/hop/ui/core/dialog/ProgressMonitorDialog.java
@@ -188,6 +188,10 @@ public class ProgressMonitorDialog {
} catch (InterruptedException e) {
interruptedException = e;
Thread.currentThread().interrupt();
+ } finally {
+ // Callers that forget monitor.done() must not leave the modal dialog up
forever.
+ // dispose() is idempotent, so callers that do call done() are
unaffected.
+ dispose();
}
}
@@ -206,6 +210,13 @@ public class ProgressMonitorDialog {
display.sleep();
}
}
+ // The worker may dispose the shell from its finally before this loop
re-checks the flags.
+ if (interruptedException != null) {
+ throw interruptedException;
+ }
+ if (targetException != null) {
+ throw targetException;
+ }
}
public Shell getShell() {
diff --git a/ui/src/main/java/org/apache/hop/ui/hopgui/HopGui.java
b/ui/src/main/java/org/apache/hop/ui/hopgui/HopGui.java
index b19c9b3f45..7c9942adc5 100644
--- a/ui/src/main/java/org/apache/hop/ui/hopgui/HopGui.java
+++ b/ui/src/main/java/org/apache/hop/ui/hopgui/HopGui.java
@@ -251,8 +251,6 @@ public class HopGui
public static final String ID_MAIN_MENU_VIEW_PARENT_ID = "25000-menu-view";
public static final String ID_MAIN_MENU_VIEW_FULL_SCREEN =
"25010-menu-view-full-screen";
- public static final String ID_MAIN_MENU_VIEW_TERMINAL =
"25010-menu-view-terminal";
- public static final String ID_MAIN_MENU_VIEW_NEW_TERMINAL =
"25020-menu-view-new-terminal";
public static final String ID_MAIN_MENU_RUN_PARENT_ID = "30000-menu-run";
public static final String ID_MAIN_MENU_RUN_START = "30010-menu-run-execute";
diff --git
a/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/metadata/MetadataPerspective.java
b/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/metadata/MetadataPerspective.java
index bba346d341..8520712f8d 100644
---
a/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/metadata/MetadataPerspective.java
+++
b/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/metadata/MetadataPerspective.java
@@ -2546,11 +2546,15 @@ public class MetadataPerspective implements
IHopPerspective, TabClosable, IMetad
if (!typeFolder.isFolder() || knownKeys.contains(key)) {
continue;
}
+ List<FileObject> jsonFiles = HopVfs.findFiles(typeFolder, "json",
false);
+ if (jsonFiles.isEmpty()) {
+ continue;
+ }
String reason =
BaseMessages.getString(PKG,
"MetadataPerspective.Unknown.NoPluginForType", key);
UnknownTypeModel unknownType =
unknownByKey.computeIfAbsent(key, k -> new UnknownTypeModel(k,
k));
- for (FileObject jsonFile : HopVfs.findFiles(typeFolder, "json",
false)) {
+ for (FileObject jsonFile : jsonFiles) {
String name =
jsonFile.getName().getBaseName().replaceAll("\\.json$", "");
// The same element can live in a parent project as well: like
anywhere else the first
// provider which has it wins, so we don't list it twice.