FINERACT-1996: Reporting should support query that includes json path for DB field in Postgres

This commit is contained in:
jmarta
2023-10-31 11:13:23 +01:00
committed by Adam Saghy
parent 24cb62443e
commit 03e71a29ff
13 changed files with 167 additions and 175 deletions
@@ -193,6 +193,8 @@ public final class DateUtils {
if (second == null) {
return 1;
}
first = first.withOffsetSameInstant(ZoneOffset.UTC);
second = second.withOffsetSameInstant(ZoneOffset.UTC);
return truncate == null ? first.compareTo(second) : first.truncatedTo(truncate).compareTo(second.truncatedTo(truncate));
}
@@ -23,8 +23,10 @@ import static java.lang.String.format;
import jakarta.validation.constraints.NotNull;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.infrastructure.dataqueries.data.ResultsetColumnHeaderData;
import org.apache.logging.log4j.util.Strings;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
@@ -251,18 +253,33 @@ public class DatabaseSpecificSQLGenerator {
.collect(Collectors.joining(", "));
}
public String buildInsert(@NotNull String definition, Collection<String> fields) {
public String buildInsert(@NotNull String definition, List<String> fields, Map<String, ResultsetColumnHeaderData> headers) {
if (fields == null || fields.isEmpty()) {
return "";
}
return "INSERT INTO " + escape(definition) + '(' + fields.stream().map(this::escape).collect(Collectors.joining(", "))
+ ") VALUES (?" + ", ?".repeat(fields.size() - 1) + ')';
+ ") VALUES (" + fields.stream().map(e -> decoratePlaceHolder(headers, e, "?")).collect(Collectors.joining(", ")) + ')';
}
public String buildUpdate(@NotNull String definition, Collection<String> fields) {
public String buildUpdate(@NotNull String definition, List<String> fields, Map<String, ResultsetColumnHeaderData> headers) {
if (fields == null || fields.isEmpty()) {
return "";
}
return "UPDATE " + escape(definition) + " SET " + fields.stream().map(e -> escape(e) + " = ?").collect(Collectors.joining(", "));
return "UPDATE " + escape(definition) + " SET "
+ fields.stream().map(e -> escape(e) + " = " + decoratePlaceHolder(headers, e, "?")).collect(Collectors.joining(", "));
}
private String decoratePlaceHolder(Map<String, ResultsetColumnHeaderData> headers, String field, String placeHolder) {
DatabaseType dialect = getDialect();
if (dialect.isPostgres()) {
ResultsetColumnHeaderData header = headers.get(field);
if (header != null) {
JdbcJavaType columnType = header.getColumnType();
if (columnType.isJsonType()) {
return placeHolder + "::" + columnType.getJdbcName(dialect);
}
}
}
return placeHolder;
}
}
@@ -74,6 +74,7 @@ public enum JdbcJavaType {
TINYTEXT(JavaType.STRING, new DialectType(JDBCType.VARCHAR, "TINYTEXT"), new DialectType(JDBCType.VARCHAR, "TEXT")), //
MEDIUMTEXT(JavaType.STRING, new DialectType(JDBCType.VARCHAR, "MEDIUMTEXT"), new DialectType(JDBCType.VARCHAR, "TEXT")), //
LONGTEXT(JavaType.STRING, new DialectType(JDBCType.VARCHAR, "LONGTEXT"), new DialectType(JDBCType.VARCHAR, "TEXT")), //
JSON(JavaType.STRING, new DialectType(JDBCType.VARCHAR, "JSON"), new DialectType(JDBCType.VARCHAR, "JSON")), //
DATE(JavaType.LOCAL_DATE, new DialectType(JDBCType.DATE), new DialectType(JDBCType.DATE)), //
// precision for TIME, TIMESTAMP (postgres) and INTERVAL specifies the number of fractional digits retained in the
// seconds field, but by default, there is no explicit bound on precision
@@ -126,7 +127,7 @@ public enum JdbcJavaType {
return javaType;
}
public static JdbcJavaType getByTypeName(@NotNull DatabaseType dialect, String name) {
public static JdbcJavaType getByTypeName(@NotNull DatabaseType dialect, String name, boolean check) {
if (name == null) {
return null;
}
@@ -145,6 +146,10 @@ public enum JdbcJavaType {
}
}
}
if (check) {
throw new PlatformServiceUnavailableException("error.msg.database.type.not.supported",
"Data type '" + name + "' is not supported ");
}
return null;
}
@@ -179,7 +184,11 @@ public enum JdbcJavaType {
}
public boolean isTextType() {
return this == TEXT || this == TINYTEXT || this == MEDIUMTEXT || this == LONGTEXT;
return this == TEXT || this == TINYTEXT || this == MEDIUMTEXT || this == LONGTEXT || this == JSON;
}
public boolean isJsonType() {
return this == JSON;
}
public boolean isSerialType() {
@@ -262,7 +271,7 @@ public enum JdbcJavaType {
public Object toJdbcValue(@NotNull DatabaseType dialect, Object value, boolean check) {
if (value != null && check && !javaType.getObjectType().matchType(value.getClass(), false)) {
throw new PlatformServiceUnavailableException("error.msg.database.type.not.allowed",
throw new PlatformServiceUnavailableException("error.msg.database.type.not.valid",
"Data type of parameter " + value + " does not match " + this);
}
return toJdbcValueImpl(dialect, value);
@@ -75,7 +75,7 @@ public final class ResultsetColumnHeaderData implements Serializable {
this.isColumnIndexed = columnIsIndexed;
// Refer org.drizzle.jdbc.internal.mysql.MySQLType.java
this.columnType = JdbcJavaType.getByTypeName(dialect, adjustColumnType(columnType));
this.columnType = JdbcJavaType.getByTypeName(dialect, adjustColumnType(columnType), true);
this.columnDisplayType = calcDisplayType();
}