# HG changeset patch
# User Martin van Dinther <martin.van.dinther@monetdbsolutions.com>
# Date 1644502350 -3600
# Node ID dd9b4fb1425684e73863f4734f3ee19e37734064
# Parent  fac0ed642af217c9905f58eda95dc00d65f8486b
Added recognition of 'xml' type.
Use default mapping to Types.VARCHAR for easy and fast (as java.lang.String) retrieval, display and setting data of columns of type 'xml'.

diff --git a/ChangeLog b/ChangeLog
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,6 +1,11 @@
 # ChangeLog file for monetdb-java
 # This file is updated with Maddlog
 
+* Thu Feb 10 2022 Martin van Dinther <martin.van.dinther@monetdbsolutions.com>
+- Added recognition of 'xml' type. Use default mapping to Types.VARCHAR for
+  easy and fast (as java.lang.String) retrieval, display and setting data of
+  columns of type 'xml'.
+
 * Thu Jan 27 2022 Martin van Dinther <martin.van.dinther@monetdbsolutions.com>
 - Compiled and released new jar files: monetdb-jdbc-3.2.jre8.jar,
   monetdb-mcl-1.21.jre8.jar and jdbcclient.jre8.jar
diff --git a/src/main/java/org/monetdb/jdbc/MonetDatabaseMetaData.java b/src/main/java/org/monetdb/jdbc/MonetDatabaseMetaData.java
--- a/src/main/java/org/monetdb/jdbc/MonetDatabaseMetaData.java
+++ b/src/main/java/org/monetdb/jdbc/MonetDatabaseMetaData.java
@@ -2964,10 +2964,10 @@ public class MonetDatabaseMetaData
 			"cast(").append(MonetDriver.getSQLTypeMap("\"sqlname\"")).append(" AS int) AS \"DATA_TYPE\", " +
 			"\"digits\" AS \"PRECISION\", " +	// note that when radix is 2 the precision shows the number of bits
 			"cast(CASE WHEN \"sqlname\" IN ('char','varchar','sec_interval','day_interval','month_interval') THEN ''''" +
-				" WHEN \"sqlname\" IN ('clob','inet','json','url','uuid','date','time','timetz','timestamp','timestamptz','blob','sqlblob') THEN \"sqlname\"||' '''" +
+				" WHEN \"sqlname\" IN ('clob','inet','json','url','uuid','date','time','timetz','timestamp','timestamptz','blob','sqlblob','xml') THEN \"sqlname\"||' '''" +
 				" ELSE NULL END AS varchar(16)) AS \"LITERAL_PREFIX\", " +
 			"cast(CASE WHEN \"sqlname\" IN ('char','varchar','sec_interval','day_interval','month_interval'" +
-						",'clob','inet','json','url','uuid','date','time','timetz','timestamp','timestamptz','blob','sqlblob') THEN ''''" +
+						",'clob','inet','json','url','uuid','date','time','timetz','timestamp','timestamptz','blob','sqlblob','xml') THEN ''''" +
 				" ELSE NULL END AS varchar(2)) AS \"LITERAL_SUFFIX\", " +
 			"CASE WHEN \"sqlname\" IN ('char','varchar') THEN 'max length'" +
 				" WHEN \"sqlname\" = 'decimal' THEN 'precision, scale'" +
@@ -2975,8 +2975,8 @@ public class MonetDatabaseMetaData
 				" ELSE NULL END AS \"CREATE_PARAMS\", " +
 			"cast(CASE WHEN \"systemname\" = 'oid' THEN ").append(DatabaseMetaData.typeNoNulls)
 				.append(" ELSE ").append(DatabaseMetaData.typeNullable).append(" END AS smallint) AS \"NULLABLE\", " +
-			"CASE WHEN \"systemname\" IN ('str','json','url') THEN true ELSE false END AS \"CASE_SENSITIVE\", " +
-			"cast(CASE WHEN \"systemname\" IN ('str','inet','json','url','uuid') THEN ").append(DatabaseMetaData.typeSearchable)
+			"CASE WHEN \"systemname\" IN ('str','json','url','xml') THEN true ELSE false END AS \"CASE_SENSITIVE\", " +
+			"cast(CASE WHEN \"systemname\" IN ('str','inet','json','url','uuid','xml') THEN ").append(DatabaseMetaData.typeSearchable)
 				.append(" ELSE ").append(DatabaseMetaData.typePredBasic).append(" END AS smallint) AS \"SEARCHABLE\", " +
 			"CASE WHEN \"sqlname\" IN ('tinyint','smallint','int','bigint','hugeint','decimal','real','double'" +
 				",'day_interval','month_interval','sec_interval') THEN false ELSE true END AS \"UNSIGNED_ATTRIBUTE\", " +
@@ -3261,8 +3261,10 @@ public class MonetDatabaseMetaData
 				" WHEN 'json' THEN 'java.lang.String'" +
 				" WHEN 'url' THEN 'org.monetdb.jdbc.types.URL'" +
 				" WHEN 'uuid' THEN 'java.lang.String'" +
+				// next UDT only when "CREATE TYPE xml EXTERNAL NAME xml;" is executed
+				" WHEN 'xml' THEN 'java.lang.String'" +
 				" ELSE 'java.lang.Object' END AS \"CLASS_NAME\", " +
-			"cast(CASE WHEN t.\"sqlname\" IN ('inet', 'json', 'url', 'uuid') THEN ").append(Types.JAVA_OBJECT)
+			"cast(CASE WHEN t.\"sqlname\" IN ('inet','json','url','uuid','xml') THEN ").append(Types.JAVA_OBJECT)
 				.append(" ELSE ").append(Types.STRUCT).append(" END AS int) AS \"DATA_TYPE\", " +
 			"t.\"systemname\" AS \"REMARKS\", " +
 			"cast(null as smallint) AS \"BASE_TYPE\" " +
diff --git a/src/main/java/org/monetdb/jdbc/MonetDriver.java.in b/src/main/java/org/monetdb/jdbc/MonetDriver.java.in
--- a/src/main/java/org/monetdb/jdbc/MonetDriver.java.in
+++ b/src/main/java/org/monetdb/jdbc/MonetDriver.java.in
@@ -352,7 +352,7 @@ public class MonetDriver implements Driv
 		typeMap.put("real", Integer.valueOf(Types.REAL));
 		typeMap.put("sec_interval", Integer.valueOf(Types.DECIMAL));
 		typeMap.put("smallint", Integer.valueOf(Types.SMALLINT));
-		typeMap.put("str", Integer.valueOf(Types.VARCHAR));	// MonetDB prepare <stmt> uses type 'str' (instead of varchar) for the schema, table and column metadata output
+		typeMap.put("str", Integer.valueOf(Types.VARCHAR));	// MonetDB prepare <stmt> uses type 'str' (instead of varchar) for the schema, table and column metadata output. DO NOT REMOVE this entry!
 		// typeMap.put("table", Integer.valueOf(Types.???));
 		typeMap.put("time", Integer.valueOf(Types.TIME));
 		typeMap.put("timestamp", Integer.valueOf(Types.TIMESTAMP));
@@ -362,7 +362,8 @@ public class MonetDriver implements Driv
 		typeMap.put("url", Integer.valueOf(Types.VARCHAR));
 		typeMap.put("uuid", Integer.valueOf(Types.VARCHAR));
 		typeMap.put("varchar", Integer.valueOf(Types.VARCHAR));
-		typeMap.put("wrd", Integer.valueOf(Types.BIGINT));  // keep it in for older MonetDB servers
+		typeMap.put("wrd", Integer.valueOf(Types.BIGINT));	// keep it in for old (pre Dec2016) MonetDB servers
+		typeMap.put("xml", Integer.valueOf(Types.VARCHAR));	// used when "CREATE TYPE xml EXTERNAL NAME xml;" is executed
 	}
 
 	/**
diff --git a/src/main/java/org/monetdb/jdbc/MonetPreparedStatement.java b/src/main/java/org/monetdb/jdbc/MonetPreparedStatement.java
--- a/src/main/java/org/monetdb/jdbc/MonetPreparedStatement.java
+++ b/src/main/java/org/monetdb/jdbc/MonetPreparedStatement.java
@@ -2336,9 +2336,13 @@ public class MonetPreparedStatement
 						}
 						castprefix = "uuid ";
 						break;
+					// case "xml":
+						// currently any string is accepted by MonetDB, so no validity check needed
+						// castprefix = "xml ";  also do NOT add a cast as MonetDB implicitly already converts a String to an xml String
+						// break;
 				}
 				if (castprefix != null) {
-					/* in specific cases prefix the string with: inet or json or url or uuid casting */
+					/* in specific cases prefix the string with: inet or json or url or uuid or xml casting */
 					setValue(parameterIndex, castprefix + MonetWrapper.sq(x));
 				} else {
 					setValue(parameterIndex, MonetWrapper.sq(x));
diff --git a/src/main/java/org/monetdb/jdbc/MonetResultSet.java b/src/main/java/org/monetdb/jdbc/MonetResultSet.java
--- a/src/main/java/org/monetdb/jdbc/MonetResultSet.java
+++ b/src/main/java/org/monetdb/jdbc/MonetResultSet.java
@@ -2064,8 +2064,8 @@ public class MonetResultSet
 				return Boolean.valueOf(val);
 			case Types.VARCHAR:
 			{
-				// The MonetDB types: inet, json, url and uuid are all mapped to Types.VARCHAR in MonetDriver.typeMap
-				// For these MonetDB types (except json, see comments below) we try to create objects of the corresponding class.
+				// The MonetDB types: inet, json, url, uuid and xml are all mapped to Types.VARCHAR in MonetDriver.typeMap
+				// For these MonetDB types (except json and xml, see comments below) we try to create objects of the corresponding class.
 				final String MonetDBType = types[columnIndex - 1];
 				switch (MonetDBType.length()) {
 				case 3:
diff --git a/tests/JDBC_API_Tester.java b/tests/JDBC_API_Tester.java
--- a/tests/JDBC_API_Tester.java
+++ b/tests/JDBC_API_Tester.java
@@ -690,6 +690,14 @@ final public class JDBC_API_Tester {
 		}
 
 		try {
+			int response = stmt.executeUpdate("CREATE TYPE xml EXTERNAL NAME xml");
+			if (response != Statement.SUCCESS_NO_INFO)
+				sb.append("Creating type xml failed to return -2!! It returned: " + response + "\n");
+		} catch (SQLException e) {
+			sb.append("failed to create type xml: ").append(e.getMessage());
+		}
+
+		try {
 			DatabaseMetaData dbmd = con.getMetaData();
 
 			// inspect the catalog by use of dbmd functions
@@ -780,7 +788,8 @@ final public class JDBC_API_Tester {
 			"null	sys	inet	org.monetdb.jdbc.types.INET	2000	inet	null\n" +
 			"null	sys	json	java.lang.String	2000	json	null\n" +
 			"null	sys	url	org.monetdb.jdbc.types.URL	2000	url	null\n" +
-			"null	sys	uuid	java.lang.String	2000	uuid	null\n");
+			"null	sys	uuid	java.lang.String	2000	uuid	null\n" +
+			"null	sys	xml	java.lang.String	2000	xml	null\n");
 
 			int[] UDTtypes = { Types.STRUCT, Types.DISTINCT };
 			compareResultSet(dbmd.getUDTs(null, "sys", null, UDTtypes), "getUDTs(null, sys, null, UDTtypes",