FINERACT-1911: Alter datatable - column update, delete fix

This commit is contained in:
jmarta
2023-08-25 10:56:09 +02:00
committed by Adam Saghy
parent 2f4705952b
commit ee31ff0ed8
6 changed files with 235 additions and 205 deletions
@@ -78,12 +78,12 @@ public enum JdbcJavaType {
// seconds field, but by default, there is no explicit bound on precision // seconds field, but by default, there is no explicit bound on precision
TIME(JavaType.LOCAL_TIME, new DialectType(JDBCType.TIME), new DialectType(JDBCType.TIME, null, "TIME WITHOUT TIME ZONE")), // TIME(JavaType.LOCAL_TIME, new DialectType(JDBCType.TIME), new DialectType(JDBCType.TIME, null, "TIME WITHOUT TIME ZONE")), //
TIME_WITH_TIMEZONE(JavaType.OFFSET_TIME, new DialectType(JDBCType.TIME_WITH_TIMEZONE, "TIME"), TIME_WITH_TIMEZONE(JavaType.OFFSET_TIME, new DialectType(JDBCType.TIME_WITH_TIMEZONE, "TIME"),
new DialectType(JDBCType.TIME_WITH_TIMEZONE, null, "TIME WITH TIME ZONE")), // new DialectType(JDBCType.TIME_WITH_TIMEZONE, "TIME WITH TIME ZONE")), //
TIMESTAMP(JavaType.LOCAL_DATETIME, new DialectType(JDBCType.TIMESTAMP), TIMESTAMP(JavaType.LOCAL_DATETIME, new DialectType(JDBCType.TIMESTAMP),
new DialectType(JDBCType.TIMESTAMP, null, "TIMESTAMP WITHOUT TIME ZONE")), // new DialectType(JDBCType.TIMESTAMP, null, "TIMESTAMP WITHOUT TIME ZONE")), //
DATETIME(JavaType.LOCAL_DATETIME, new DialectType(JDBCType.TIMESTAMP, "DATETIME"), new DialectType(JDBCType.TIMESTAMP)), // DATETIME(JavaType.LOCAL_DATETIME, new DialectType(JDBCType.TIMESTAMP, "DATETIME"), new DialectType(JDBCType.TIMESTAMP)), //
TIMESTAMP_WITH_TIMEZONE(JavaType.OFFSET_DATETIME, new DialectType(JDBCType.TIMESTAMP_WITH_TIMEZONE, null, "DATETIME"), TIMESTAMP_WITH_TIMEZONE(JavaType.OFFSET_DATETIME, new DialectType(JDBCType.TIMESTAMP_WITH_TIMEZONE, "DATETIME"),
new DialectType(JDBCType.TIMESTAMP_WITH_TIMEZONE, null, "TIMESTAMP WITH TIME ZONE", "TIMESTAMPTZ")), // new DialectType(JDBCType.TIMESTAMP_WITH_TIMEZONE, "TIMESTAMP WITH TIME ZONE", "TIMESTAMPTZ")), //
INTERVAL(JavaType.TIME, new DialectType(JDBCType.TIME), new DialectType(JDBCType.TIME, "INTERVAL")), // INTERVAL(JavaType.TIME, new DialectType(JDBCType.TIME), new DialectType(JDBCType.TIME, "INTERVAL")), //
BINARY(JavaType.BINARY, new DialectType(JDBCType.BINARY, true), new DialectType(JDBCType.BINARY, "BYTEA")), // BINARY(JavaType.BINARY, new DialectType(JDBCType.BINARY, true), new DialectType(JDBCType.BINARY, "BYTEA")), //
VARBINARY(JavaType.BINARY, new DialectType(JDBCType.VARBINARY, true), new DialectType(JDBCType.VARBINARY, "BYTEA")), // VARBINARY(JavaType.BINARY, new DialectType(JDBCType.VARBINARY, true), new DialectType(JDBCType.VARBINARY, "BYTEA")), //
@@ -54,8 +54,10 @@ public class PostgreSQLQueryService implements DatabaseQueryService {
@Override @Override
public SqlRowSet getTableColumns(DataSource dataSource, String tableName) { public SqlRowSet getTableColumns(DataSource dataSource, String tableName) {
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
String sql = "SELECT attname AS COLUMN_NAME, not attnotnull AS IS_NULLABLE, atttypid::regtype AS DATATYPE, attlen AS CHARACTER_MAXIMUM_LENGTH, attnum = 1 AS COLUMN_KEY FROM pg_attribute WHERE attrelid = '\"" String sql = "SELECT column_name, is_nullable, data_type,"
+ tableName + "\"'::regclass AND attnum > 0 AND NOT attisdropped ORDER BY attnum"; + " coalesce(character_maximum_length, numeric_precision, datetime_precision) AS max_length, ordinal_position = 1 AS column_key"
+ " FROM information_schema.columns WHERE table_catalog = current_catalog AND table_schema = current_schema AND table_name = '"
+ tableName + "' ORDER BY ordinal_position";
final SqlRowSet columnDefinitions = jdbcTemplate.queryForRowSet(sql); // NOSONAR final SqlRowSet columnDefinitions = jdbcTemplate.queryForRowSet(sql); // NOSONAR
if (columnDefinitions.next()) { if (columnDefinitions.next()) {
return columnDefinitions; return columnDefinitions;
@@ -520,7 +520,6 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
} }
private String datatableColumnNameToCodeValueName(final String columnName, final String code) { private String datatableColumnNameToCodeValueName(final String columnName, final String code) {
return code + "_cd_" + columnName; return code + "_cd_" + columnName;
} }
@@ -537,17 +536,17 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
String name = column.has(API_FIELD_NAME) ? column.get(API_FIELD_NAME).getAsString() : null; String name = column.has(API_FIELD_NAME) ? column.get(API_FIELD_NAME).getAsString() : null;
final String type = column.has(API_FIELD_TYPE) ? column.get(API_FIELD_TYPE).getAsString().toLowerCase() : null; final String type = column.has(API_FIELD_TYPE) ? column.get(API_FIELD_TYPE).getAsString().toLowerCase() : null;
final Integer length = column.has(API_FIELD_LENGTH) ? column.get(API_FIELD_LENGTH).getAsInt() : null; final Integer length = column.has(API_FIELD_LENGTH) ? column.get(API_FIELD_LENGTH).getAsInt() : null;
final Boolean mandatory = column.has(API_FIELD_MANDATORY) ? column.get(API_FIELD_MANDATORY).getAsBoolean() : false; final boolean mandatory = column.has(API_FIELD_MANDATORY) && column.get(API_FIELD_MANDATORY).getAsBoolean();
final Boolean unique = column.has(API_FIELD_UNIQUE) ? column.get(API_FIELD_UNIQUE).getAsBoolean() : false; final boolean unique = column.has(API_FIELD_UNIQUE) && column.get(API_FIELD_UNIQUE).getAsBoolean();
final String code = column.has(API_FIELD_CODE) ? column.get(API_FIELD_CODE).getAsString() : null; final String code = column.has(API_FIELD_CODE) ? column.get(API_FIELD_CODE).getAsString() : null;
if (StringUtils.isNotBlank(code)) { if (StringUtils.isNotBlank(code)) {
if (isConstraintApproach) { if (isConstraintApproach) {
codeMappings.put(dataTableNameAlias + "_" + name, this.codeReadPlatformService.retriveCode(code).getId()); codeMappings.put(dataTableNameAlias + "_" + name, this.codeReadPlatformService.retriveCode(code).getId());
String fkName = "fk_" + dataTableNameAlias + "_" + name; String fkName = "fk_" + dataTableNameAlias + "_" + name;
constrainBuilder.append(", CONSTRAINT ").append(sqlGenerator.escape(fkName)).append(" ") constrainBuilder.append(", CONSTRAINT ").append(sqlGenerator.escape(fkName)).append(" ").append("FOREIGN KEY (")
.append("FOREIGN KEY (" + sqlGenerator.escape(name) + ") ").append("REFERENCES ") .append(sqlGenerator.escape(name)).append(") ").append("REFERENCES ").append(sqlGenerator.escape(CODE_VALUES_TABLE))
.append(sqlGenerator.escape(CODE_VALUES_TABLE)).append(" (id)"); .append(" (id)");
} else { } else {
name = datatableColumnNameToCodeValueName(name, code); name = datatableColumnNameToCodeValueName(name, code);
} }
@@ -602,7 +601,7 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
final String dataTableNameAlias = datatableName.toLowerCase().replaceAll("\\s", "_"); final String dataTableNameAlias = datatableName.toLowerCase().replaceAll("\\s", "_");
final String fkName = dataTableNameAlias + "_" + fkColumnName; final String fkName = dataTableNameAlias + "_" + fkColumnName;
StringBuilder sqlBuilder = new StringBuilder(); StringBuilder sqlBuilder = new StringBuilder();
sqlBuilder.append("CREATE TABLE " + sqlGenerator.escape(datatableName) + " ("); sqlBuilder.append("CREATE TABLE ").append(sqlGenerator.escape(datatableName)).append(" (");
if (multiRow) { if (multiRow) {
if (databaseTypeResolver.isMySQL()) { if (databaseTypeResolver.isMySQL()) {
@@ -614,7 +613,7 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
throw new IllegalStateException("Current database is not supported"); throw new IllegalStateException("Current database is not supported");
} }
} }
sqlBuilder.append(sqlGenerator.escape(fkColumnName) + " BIGINT NOT NULL, "); sqlBuilder.append(sqlGenerator.escape(fkColumnName)).append(" BIGINT NOT NULL, ");
// Add Created At and Updated At // Add Created At and Updated At
columns.add(addColumn(DataTableApiConstant.CREATEDAT_FIELD_NAME, DATETIME, false, null, false, false)); columns.add(addColumn(DataTableApiConstant.CREATEDAT_FIELD_NAME, DATETIME, false, null, false, false));
@@ -628,25 +627,23 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
} }
// Remove trailing comma and space // Remove trailing comma and space
sqlBuilder = sqlBuilder.delete(sqlBuilder.length() - 2, sqlBuilder.length()); sqlBuilder.delete(sqlBuilder.length() - 2, sqlBuilder.length());
String fullFkName = "fk_" + fkName; String fullFkName = "fk_" + fkName;
if (multiRow) { if (multiRow) {
sqlBuilder.append(", PRIMARY KEY (").append(TABLE_FIELD_ID).append(")"); sqlBuilder.append(", PRIMARY KEY (").append(TABLE_FIELD_ID).append(")");
if (databaseTypeResolver.isMySQL()) { if (databaseTypeResolver.isMySQL()) {
sqlBuilder sqlBuilder.append(", KEY ").append(sqlGenerator.escape("fk_" + fkColumnName)).append(" (")
.append(", KEY " + sqlGenerator.escape("fk_" + fkColumnName) + " (" + sqlGenerator.escape(fkColumnName) + ")"); .append(sqlGenerator.escape(fkColumnName)).append(")");
} }
sqlBuilder.append(", CONSTRAINT " + sqlGenerator.escape(fullFkName) + " ") sqlBuilder.append(", CONSTRAINT ").append(sqlGenerator.escape(fullFkName)).append(" FOREIGN KEY (")
.append("FOREIGN KEY (" + sqlGenerator.escape(fkColumnName) + ") ") .append(sqlGenerator.escape(fkColumnName)).append(") ").append("REFERENCES ")
.append("REFERENCES " + sqlGenerator.escape(entityTable.getApptableName()) + " (").append(TABLE_FIELD_ID) .append(sqlGenerator.escape(entityTable.getApptableName())).append(" (").append(TABLE_FIELD_ID).append(")");
.append(")");
} else { } else {
sqlBuilder.append(", PRIMARY KEY (" + sqlGenerator.escape(fkColumnName) + ")") sqlBuilder.append(", PRIMARY KEY (").append(sqlGenerator.escape(fkColumnName)).append(")").append(", CONSTRAINT ")
.append(", CONSTRAINT " + sqlGenerator.escape(fullFkName) + " ") .append(sqlGenerator.escape(fullFkName)).append(" FOREIGN KEY (").append(sqlGenerator.escape(fkColumnName))
.append("FOREIGN KEY (" + sqlGenerator.escape(fkColumnName) + ") ") .append(") ").append("REFERENCES ").append(sqlGenerator.escape(entityTable.getApptableName())).append(" (")
.append("REFERENCES " + sqlGenerator.escape(entityTable.getApptableName()) + " (").append(TABLE_FIELD_ID) .append(TABLE_FIELD_ID).append(")");
.append(")");
} }
sqlBuilder.append(constrainBuilder); sqlBuilder.append(constrainBuilder);
@@ -661,12 +658,10 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
// create indexes // create indexes
if (multiRow) { if (multiRow) {
createFkIndex(datatableName, fkColumnName); createFkIndex(datatableName, fkColumnName);
} else {
/*
* in case of non-multirow, the primary key of the table is the FK and MySQL and PostgreSQL
* automatically puts an index onto it so no need to create it explicitly
*/
} }
// in case of non-multirow, the primary key of the table is the FK and MySQL and PostgreSQL
// automatically puts an index onto it so no need to create it explicitly
createIndexesForTable(datatableName, columns); createIndexesForTable(datatableName, columns);
registerDatatable(datatableName, entityName, entitySubType); registerDatatable(datatableName, entityName, entitySubType);
registerColumnCodeMapping(codeMappings); registerColumnCodeMapping(codeMappings);
@@ -705,13 +700,11 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
private void createIndexForColumn(String datatableName, JsonObject column) { private void createIndexForColumn(String datatableName, JsonObject column) {
String name = column.has(API_FIELD_NAME) ? column.get(API_FIELD_NAME).getAsString() : null; String name = column.has(API_FIELD_NAME) ? column.get(API_FIELD_NAME).getAsString() : null;
final Boolean unique = column.has(API_FIELD_UNIQUE) ? column.get(API_FIELD_UNIQUE).getAsBoolean() : false; final boolean unique = column.has(API_FIELD_UNIQUE) && column.get(API_FIELD_UNIQUE).getAsBoolean();
final Boolean indexed = column.has(API_FIELD_INDEXED) ? column.get(API_FIELD_INDEXED).getAsBoolean() : false; final boolean indexed = column.has(API_FIELD_INDEXED) && column.get(API_FIELD_INDEXED).getAsBoolean();
if (indexed) { if (!unique && indexed) {
if (!unique) { String indexName = datatableKeywordGenerator.generateIndexName(datatableName, name);
String indexName = datatableKeywordGenerator.generateIndexName(datatableName, name); createIndex(indexName, datatableName, name);
createIndex(indexName, datatableName, name);
}
} }
} }
@@ -719,7 +712,7 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
KeyHolder keyHolder = new GeneratedKeyHolder(); KeyHolder keyHolder = new GeneratedKeyHolder();
int insertsCount = this.jdbcTemplate.update(c -> c.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS), keyHolder); int insertsCount = this.jdbcTemplate.update(c -> c.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS), keyHolder);
if (insertsCount == 1) { if (insertsCount == 1) {
Number assignedKey = null; Number assignedKey;
if (keyHolder.getKeys().size() > 1) { if (keyHolder.getKeys().size() > 1) {
assignedKey = (Long) keyHolder.getKeys().get(TABLE_FIELD_ID); assignedKey = (Long) keyHolder.getKeys().get(TABLE_FIELD_ID);
} else { } else {
@@ -734,48 +727,52 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
} }
private void parseDatatableColumnForUpdate(final JsonObject column, private void parseDatatableColumnForUpdate(final JsonObject column,
final Map<String, ResultsetColumnHeaderData> mapColumnNameDefinition, StringBuilder sqlBuilder, final String datatableName, final Map<String, ResultsetColumnHeaderData> mapColumnNameDefinition, final String datatableName, StringBuilder renameBuilder,
final StringBuilder constrainBuilder, final Map<String, Long> codeMappings, final List<String> removeMappings, StringBuilder changeBuilder, final StringBuilder constrainBuilder, final Map<String, Long> codeMappings,
final boolean isConstraintApproach) { final List<String> removeMappings, final boolean isConstraintApproach) {
String name = column.has(API_FIELD_NAME) ? column.get(API_FIELD_NAME).getAsString() : null; String oldName = column.has(API_FIELD_NAME) ? column.get(API_FIELD_NAME).getAsString() : null;
if (!mapColumnNameDefinition.containsKey(oldName)) {
throw new PlatformDataIntegrityException("error.msg.datatable.column.missing.update.parse",
"Column " + oldName + " does not exist.", oldName);
}
final String lengthStr = column.has(API_FIELD_LENGTH) ? column.get(API_FIELD_LENGTH).getAsString() : null; final String lengthStr = column.has(API_FIELD_LENGTH) ? column.get(API_FIELD_LENGTH).getAsString() : null;
Integer length = StringUtils.isNotBlank(lengthStr) ? Integer.parseInt(lengthStr) : null; Long length = StringUtils.isNotBlank(lengthStr) ? Long.parseLong(lengthStr) : null;
String newName = column.has(API_FIELD_NEWNAME) ? column.get(API_FIELD_NEWNAME).getAsString() : name; String newName = column.has(API_FIELD_NEWNAME) ? column.get(API_FIELD_NEWNAME).getAsString() : null;
final Boolean mandatory = column.has(API_FIELD_MANDATORY) ? column.get(API_FIELD_MANDATORY).getAsBoolean() : false; final Boolean newMandatory = column.has(API_FIELD_MANDATORY) ? column.get(API_FIELD_MANDATORY).getAsBoolean() : null;
final String after = column.has(API_FIELD_AFTER) ? column.get(API_FIELD_AFTER).getAsString() : null; final String after = column.has(API_FIELD_AFTER) ? column.get(API_FIELD_AFTER).getAsString() : null;
final String code = column.has(API_FIELD_CODE) ? column.get(API_FIELD_CODE).getAsString() : null; final String code = column.has(API_FIELD_CODE) ? column.get(API_FIELD_CODE).getAsString() : null;
final String newCode = column.has(API_FIELD_NEWCODE) ? column.get(API_FIELD_NEWCODE).getAsString() : null; final String newCode = column.has(API_FIELD_NEWCODE) ? column.get(API_FIELD_NEWCODE).getAsString() : null;
final String dataTableNameAlias = datatableName.toLowerCase().replaceAll("\\s", "_"); final String dataTableNameAlias = datatableName.toLowerCase().replaceAll("\\s", "_");
if (isConstraintApproach) { if (isConstraintApproach) {
if (StringUtils.isBlank(newName)) { if (StringUtils.isBlank(newName)) {
newName = name; newName = oldName;
} }
String fkName = "fk_" + dataTableNameAlias + "_" + name; String fkName = "fk_" + dataTableNameAlias + "_" + oldName;
String newFkName = "fk_" + dataTableNameAlias + "_" + newName; String newFkName = "fk_" + dataTableNameAlias + "_" + newName;
if (!StringUtils.equalsIgnoreCase(code, newCode) || !StringUtils.equalsIgnoreCase(name, newName)) { if (!StringUtils.equalsIgnoreCase(code, newCode) || !StringUtils.equalsIgnoreCase(oldName, newName)) {
if (StringUtils.equalsIgnoreCase(code, newCode)) { if (StringUtils.equalsIgnoreCase(code, newCode)) {
final int codeId = getCodeIdForColumn(dataTableNameAlias, name); final int codeId = getCodeIdForColumn(dataTableNameAlias, oldName);
if (codeId > 0) { if (codeId > 0) {
removeMappings.add(dataTableNameAlias + "_" + name); removeMappings.add(dataTableNameAlias + "_" + oldName);
constrainBuilder.append(", DROP FOREIGN KEY ").append(sqlGenerator.escape(fkName)).append(" "); constrainBuilder.append(", DROP CONSTRAINT ").append(sqlGenerator.escape(fkName)).append(" ");
codeMappings.put(dataTableNameAlias + "_" + newName, (long) codeId); codeMappings.put(dataTableNameAlias + "_" + newName, (long) codeId);
constrainBuilder.append(",ADD CONSTRAINT ").append(sqlGenerator.escape(newFkName)).append(" ") constrainBuilder.append(", ADD CONSTRAINT ").append(sqlGenerator.escape(newFkName)).append(" ")
.append("FOREIGN KEY (" + sqlGenerator.escape(newName) + ") ").append("REFERENCES ") .append("FOREIGN KEY (").append(sqlGenerator.escape(newName)).append(") ").append("REFERENCES ")
.append(sqlGenerator.escape(CODE_VALUES_TABLE)).append(" (").append(TABLE_FIELD_ID).append(")"); .append(sqlGenerator.escape(CODE_VALUES_TABLE)).append(" (").append(TABLE_FIELD_ID).append(")");
} }
} else { } else {
if (code != null) { if (code != null) {
removeMappings.add(dataTableNameAlias + "_" + name); removeMappings.add(dataTableNameAlias + "_" + oldName);
if (newCode == null || !StringUtils.equalsIgnoreCase(name, newName)) { if (newCode == null || !StringUtils.equalsIgnoreCase(oldName, newName)) {
constrainBuilder.append(", DROP FOREIGN KEY ").append(sqlGenerator.escape(fkName)).append(" "); constrainBuilder.append(", DROP CONSTRAINT ").append(sqlGenerator.escape(fkName)).append(" ");
} }
} }
if (newCode != null) { if (newCode != null) {
codeMappings.put(dataTableNameAlias + "_" + newName, this.codeReadPlatformService.retriveCode(newCode).getId()); codeMappings.put(dataTableNameAlias + "_" + newName, this.codeReadPlatformService.retriveCode(newCode).getId());
if (code == null || !StringUtils.equalsIgnoreCase(name, newName)) { if (code == null || !StringUtils.equalsIgnoreCase(oldName, newName)) {
constrainBuilder.append(",ADD CONSTRAINT ").append(sqlGenerator.escape(newFkName)).append(" ") constrainBuilder.append(", ADD CONSTRAINT ").append(sqlGenerator.escape(newFkName)).append(" ")
.append("FOREIGN KEY (" + sqlGenerator.escape(newName) + ") ").append("REFERENCES ") .append("FOREIGN KEY (").append(sqlGenerator.escape(newName)).append(") ").append("REFERENCES ")
.append(sqlGenerator.escape(CODE_VALUES_TABLE)).append(" (").append(TABLE_FIELD_ID).append(")"); .append(sqlGenerator.escape(CODE_VALUES_TABLE)).append(" (").append(TABLE_FIELD_ID).append(")");
} }
} }
@@ -783,7 +780,7 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
} }
} else { } else {
if (StringUtils.isNotBlank(code)) { if (StringUtils.isNotBlank(code)) {
name = datatableColumnNameToCodeValueName(name, code); oldName = datatableColumnNameToCodeValueName(oldName, code);
if (StringUtils.isNotBlank(newCode)) { if (StringUtils.isNotBlank(newCode)) {
newName = datatableColumnNameToCodeValueName(newName, newCode); newName = datatableColumnNameToCodeValueName(newName, newCode);
} else { } else {
@@ -791,37 +788,44 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
} }
} }
} }
if (!mapColumnNameDefinition.containsKey(name)) {
throw new PlatformDataIntegrityException("error.msg.datatable.column.missing.update.parse",
"Column " + name + " does not exist.", name);
}
final JdbcJavaType type = mapColumnNameDefinition.get(name).getColumnType();
DatabaseType dialect = databaseTypeResolver.databaseType(); DatabaseType dialect = databaseTypeResolver.databaseType();
if (length == null && type.hasPrecision(dialect)) { ResultsetColumnHeaderData columnHeader = mapColumnNameDefinition.get(oldName);
Long columnLength = mapColumnNameDefinition.get(name).getColumnLength(); final JdbcJavaType type = columnHeader.getColumnType();
length = columnLength == null ? null : columnLength.intValue(); boolean nameChanged = !StringUtils.isBlank(newName) && !newName.equals(oldName);
} boolean lengthChanged = length != null && !length.equals(columnHeader.getColumnLength()) && type.hasPrecision(dialect);
boolean nullityChanged = newMandatory != null && newMandatory != columnHeader.isMandatory();
if (databaseTypeResolver.isMySQL()) { boolean afterChanged = after != null && databaseTypeResolver.isMySQL();
sqlBuilder.append(", CHANGE ").append(sqlGenerator.escape(name)).append(" ").append(sqlGenerator.escape(newName)).append(" ") if (nameChanged || lengthChanged || nullityChanged || afterChanged) {
.append(type); Integer precision = length == null ? null : length.intValue();
} else if (databaseTypeResolver.isPostgreSQL()) { Integer scale = null;
sqlBuilder.append(", RENAME ").append(sqlGenerator.escape(name)).append(" TO ").append(sqlGenerator.escape(newName));
}
if (length != null && length > 0) {
if (type.isDecimalType()) { if (type.isDecimalType()) {
sqlBuilder.append(" ").append(type.formatSql(dialect, 19, 6)); precision = 19;
scale = 6;
}
String colName = StringUtils.isBlank(newName) ? oldName : newName;
boolean mandatory = newMandatory == null ? columnHeader.isMandatory() : newMandatory;
if (databaseTypeResolver.isMySQL()) {
String modifySql = nameChanged ? ("CHANGE " + sqlGenerator.escape(oldName) + " " + sqlGenerator.escape(colName))
: (" MODIFY " + sqlGenerator.escape(colName));
changeBuilder.append(", ").append(modifySql).append(" ").append(type.formatSql(dialect, precision, scale))
.append(mandatory ? " NOT NULL" : " DEFAULT NULL");
if (after != null) {
changeBuilder.append(" AFTER ").append(sqlGenerator.escape(after));
}
} else { } else {
sqlBuilder.append(" ").append(type.formatSql(dialect, length)); if (nameChanged) {
renameBuilder.append("ALTER TABLE ").append(sqlGenerator.escape(datatableName)).append(" RENAME COLUMN ")
.append(sqlGenerator.escape(oldName)).append(" TO ").append(sqlGenerator.escape(newName)).append("; ");
}
if (lengthChanged) {
changeBuilder.append(", ALTER ").append(sqlGenerator.escape(colName)).append(" type ")
.append(type.formatSql(dialect, precision, scale));
}
if (nullityChanged) {
changeBuilder.append(", ALTER ").append(sqlGenerator.escape(colName))
.append(mandatory ? " set not null" : " drop not null");
}
} }
}
if (databaseTypeResolver.isMySQL()) {
sqlBuilder.append(mandatory ? " NOT NULL" : " DEFAULT NULL");
}
if (after != null) {
sqlBuilder.append(" AFTER " + sqlGenerator.escape(after));
} }
} }
@@ -844,8 +848,8 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
String name = column.has(API_FIELD_NAME) ? column.get(API_FIELD_NAME).getAsString() : null; String name = column.has(API_FIELD_NAME) ? column.get(API_FIELD_NAME).getAsString() : null;
final String type = column.has(API_FIELD_TYPE) ? column.get(API_FIELD_TYPE).getAsString().toLowerCase() : null; final String type = column.has(API_FIELD_TYPE) ? column.get(API_FIELD_TYPE).getAsString().toLowerCase() : null;
final Integer length = column.has(API_FIELD_LENGTH) ? column.get(API_FIELD_LENGTH).getAsInt() : null; final Integer length = column.has(API_FIELD_LENGTH) ? column.get(API_FIELD_LENGTH).getAsInt() : null;
final Boolean mandatory = column.has(API_FIELD_MANDATORY) ? column.get(API_FIELD_MANDATORY).getAsBoolean() : false; final boolean mandatory = column.has(API_FIELD_MANDATORY) && column.get(API_FIELD_MANDATORY).getAsBoolean();
final Boolean unique = column.has(API_FIELD_UNIQUE) ? column.get(API_FIELD_UNIQUE).getAsBoolean() : false; final boolean unique = column.has(API_FIELD_UNIQUE) && column.get(API_FIELD_UNIQUE).getAsBoolean();
final String after = column.has(API_FIELD_AFTER) ? column.get(API_FIELD_AFTER).getAsString() : null; final String after = column.has(API_FIELD_AFTER) ? column.get(API_FIELD_AFTER).getAsString() : null;
final String code = column.has(API_FIELD_CODE) ? column.get(API_FIELD_CODE).getAsString() : null; final String code = column.has(API_FIELD_CODE) ? column.get(API_FIELD_CODE).getAsString() : null;
@@ -853,9 +857,9 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
if (isConstraintApproach) { if (isConstraintApproach) {
String fkName = "fk_" + dataTableNameAlias + "_" + name; String fkName = "fk_" + dataTableNameAlias + "_" + name;
codeMappings.put(dataTableNameAlias + "_" + name, this.codeReadPlatformService.retriveCode(code).getId()); codeMappings.put(dataTableNameAlias + "_" + name, this.codeReadPlatformService.retriveCode(code).getId());
constrainBuilder.append(",ADD CONSTRAINT ").append(sqlGenerator.escape(fkName)).append(" ") constrainBuilder.append(",ADD CONSTRAINT ").append(sqlGenerator.escape(fkName)).append(" ").append("FOREIGN KEY (")
.append("FOREIGN KEY (" + sqlGenerator.escape(name) + ") ").append("REFERENCES ") .append(sqlGenerator.escape(name)).append(") ").append("REFERENCES ").append(sqlGenerator.escape(CODE_VALUES_TABLE))
.append(sqlGenerator.escape(CODE_VALUES_TABLE)).append(" (").append(TABLE_FIELD_ID).append(")"); .append(" (").append(TABLE_FIELD_ID).append(")");
} else { } else {
name = datatableColumnNameToCodeValueName(name, code); name = datatableColumnNameToCodeValueName(name, code);
} }
@@ -864,8 +868,8 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
if (unique) { if (unique) {
String uniqueKeyName = datatableKeywordGenerator.generateUniqueKeyName(dataTableNameAlias, name); String uniqueKeyName = datatableKeywordGenerator.generateUniqueKeyName(dataTableNameAlias, name);
constrainBuilder.append(",ADD CONSTRAINT ").append(sqlGenerator.escape(uniqueKeyName)).append(" ") constrainBuilder.append(",ADD CONSTRAINT ").append(sqlGenerator.escape(uniqueKeyName)).append(" ").append("UNIQUE (")
.append("UNIQUE (" + sqlGenerator.escape(name) + ")"); .append(sqlGenerator.escape(name)).append(")");
} }
if (mandatory) { if (mandatory) {
@@ -875,7 +879,7 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
} }
if (after != null) { if (after != null) {
sqlBuilder.append(" AFTER " + sqlGenerator.escape(after)); sqlBuilder.append(" AFTER ").append(sqlGenerator.escape(after));
} }
} }
@@ -883,14 +887,20 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
final StringBuilder constrainBuilder, final List<String> codeMappings) { final StringBuilder constrainBuilder, final List<String> codeMappings) {
final String datatableAlias = datatableName.toLowerCase().replaceAll("\\s", "_"); final String datatableAlias = datatableName.toLowerCase().replaceAll("\\s", "_");
final String name = column.has(API_FIELD_NAME) ? column.get(API_FIELD_NAME).getAsString() : null; final String name = column.has(API_FIELD_NAME) ? column.get(API_FIELD_NAME).getAsString() : null;
final StringBuilder findFKSql = new StringBuilder(); if (name == null) {
findFKSql.append("SELECT count(*)").append("FROM information_schema.TABLE_CONSTRAINTS i") throw new GeneralPlatformDomainRuleException("error.msg.missing.datatable.column.name",
.append(" WHERE i.CONSTRAINT_TYPE = 'FOREIGN KEY'").append(" AND i.TABLE_SCHEMA = DATABASE()") "Datatable column name to drop is missing.");
.append(" AND i.TABLE_NAME = '").append(datatableName).append("' AND i.CONSTRAINT_NAME = 'fk_").append(datatableAlias) }
.append("_").append(name).append("' "); sqlBuilder.append(", DROP COLUMN ").append(sqlGenerator.escape(name)).append(" ");
final int count = this.jdbcTemplate.queryForObject(findFKSql.toString(), Integer.class);
String fkName = "fk_" + datatableAlias + "_" + name;
String schemaSql = databaseTypeResolver.isMySQL() ? "i.TABLE_SCHEMA = SCHEMA()"
: "i.table_catalog = current_catalog AND i.table_schema = current_schema";
String findFKSql = "SELECT count(*) FROM information_schema.TABLE_CONSTRAINTS i" + " WHERE i.CONSTRAINT_TYPE = 'FOREIGN KEY' AND "
+ schemaSql + " AND i.TABLE_NAME = '" + datatableName + "' AND i.CONSTRAINT_NAME = '" + fkName + "' ";
final int count = this.jdbcTemplate.queryForObject(findFKSql, Integer.class);
if (count > 0) { if (count > 0) {
String fkName = "fk_" + datatableAlias + "_" + name;
codeMappings.add(datatableAlias + "_" + name); codeMappings.add(datatableAlias + "_" + name);
constrainBuilder.append(", DROP FOREIGN KEY ").append(sqlGenerator.escape(fkName)).append(" "); constrainBuilder.append(", DROP FOREIGN KEY ").append(sqlGenerator.escape(fkName)).append(" ");
} }
@@ -985,25 +995,22 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
String fullNewFk = "fk_" + newFKName; String fullNewFk = "fk_" + newFKName;
String fullNewConstraint = "fk_" + newConstraintName; String fullNewConstraint = "fk_" + newConstraintName;
if (mapColumnNameDefinition.containsKey(TABLE_FIELD_ID)) { if (mapColumnNameDefinition.containsKey(TABLE_FIELD_ID)) {
sqlBuilder.append("ALTER TABLE " + sqlGenerator.escape(datatableName) + " ") sqlBuilder.append("ALTER TABLE ").append(sqlGenerator.escape(datatableName)).append(" DROP KEY ")
.append("DROP KEY " + sqlGenerator.escape(fullOldFk) + ",") .append(sqlGenerator.escape(fullOldFk)).append(",").append("DROP FOREIGN KEY ")
.append("DROP FOREIGN KEY " + sqlGenerator.escape(fullOldConstraint) + ",") .append(sqlGenerator.escape(fullOldConstraint)).append(",").append("CHANGE COLUMN ")
.append("CHANGE COLUMN " + sqlGenerator.escape(oldFKName) + " " + sqlGenerator.escape(newFKName) .append(sqlGenerator.escape(oldFKName)).append(" ").append(sqlGenerator.escape(newFKName))
+ " BIGINT NOT NULL,") .append(" BIGINT NOT NULL,").append("ADD KEY ").append(sqlGenerator.escape(fullNewFk)).append(" (")
.append("ADD KEY " + sqlGenerator.escape(fullNewFk) + " (" + sqlGenerator.escape(newFKName) + "),") .append(sqlGenerator.escape(newFKName)).append("),").append("ADD CONSTRAINT ")
.append("ADD CONSTRAINT " + sqlGenerator.escape(fullNewConstraint) + " ") .append(sqlGenerator.escape(fullNewConstraint)).append(" FOREIGN KEY (")
.append("FOREIGN KEY (" + sqlGenerator.escape(newFKName) + ") ") .append(sqlGenerator.escape(newFKName)).append(") ").append("REFERENCES ")
.append("REFERENCES " + sqlGenerator.escape(entityTable.getApptableName()) + " (").append(TABLE_FIELD_ID) .append(sqlGenerator.escape(entityTable.getApptableName())).append(" (").append(TABLE_FIELD_ID).append(")");
.append(")");
} else { } else {
sqlBuilder.append("ALTER TABLE " + sqlGenerator.escape(datatableName) + " ") sqlBuilder.append("ALTER TABLE ").append(sqlGenerator.escape(datatableName)).append(" DROP FOREIGN KEY ")
.append("DROP FOREIGN KEY " + sqlGenerator.escape(fullOldConstraint) + ",") .append(sqlGenerator.escape(fullOldConstraint)).append(",").append("CHANGE COLUMN ")
.append("CHANGE COLUMN " + sqlGenerator.escape(oldFKName) + " " + sqlGenerator.escape(newFKName) .append(sqlGenerator.escape(oldFKName)).append(" ").append(sqlGenerator.escape(newFKName))
+ " BIGINT NOT NULL,") .append(" BIGINT NOT NULL,").append("ADD CONSTRAINT ").append(sqlGenerator.escape(fullNewConstraint))
.append("ADD CONSTRAINT " + sqlGenerator.escape(fullNewConstraint) + " ") .append(" FOREIGN KEY (").append(sqlGenerator.escape(newFKName)).append(") ").append("REFERENCES ")
.append("FOREIGN KEY (" + sqlGenerator.escape(newFKName) + ") ") .append(sqlGenerator.escape(entityTable.getApptableName())).append(" (").append(TABLE_FIELD_ID).append(")");
.append("REFERENCES " + sqlGenerator.escape(entityTable.getApptableName()) + " (").append(TABLE_FIELD_ID)
.append(")");
} }
this.jdbcTemplate.execute(sqlBuilder.toString()); this.jdbcTemplate.execute(sqlBuilder.toString());
@@ -1032,14 +1039,13 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
// Remove the first comma, right after ALTER TABLE datatable // Remove the first comma, right after ALTER TABLE datatable
final int indexOfFirstComma = sqlBuilder.indexOf(","); final int indexOfFirstComma = sqlBuilder.indexOf(",");
if (indexOfFirstComma != -1) { if (indexOfFirstComma != -1) {
sqlBuilder = sqlBuilder.deleteCharAt(indexOfFirstComma); sqlBuilder.deleteCharAt(indexOfFirstComma);
} }
sqlBuilder.append(constrainBuilder); sqlBuilder.append(constrainBuilder);
this.jdbcTemplate.execute(sqlBuilder.toString()); this.jdbcTemplate.execute(sqlBuilder.toString());
deleteColumnCodeMapping(codeMappings); deleteColumnCodeMapping(codeMappings);
} }
if (addColumns != null) { if (addColumns != null) {
StringBuilder sqlBuilder = new StringBuilder("ALTER TABLE " + sqlGenerator.escape(datatableName)); StringBuilder sqlBuilder = new StringBuilder("ALTER TABLE " + sqlGenerator.escape(datatableName));
final StringBuilder constrainBuilder = new StringBuilder(); final StringBuilder constrainBuilder = new StringBuilder();
final Map<String, Long> codeMappings = new HashMap<>(); final Map<String, Long> codeMappings = new HashMap<>();
@@ -1056,7 +1062,7 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
// Remove the first comma, right after ALTER TABLE datatable // Remove the first comma, right after ALTER TABLE datatable
final int indexOfFirstComma = sqlBuilder.indexOf(","); final int indexOfFirstComma = sqlBuilder.indexOf(",");
if (indexOfFirstComma != -1) { if (indexOfFirstComma != -1) {
sqlBuilder = sqlBuilder.deleteCharAt(indexOfFirstComma); sqlBuilder.deleteCharAt(indexOfFirstComma);
} }
sqlBuilder.append(constrainBuilder); sqlBuilder.append(constrainBuilder);
jdbcTemplate.execute(sqlBuilder.toString()); jdbcTemplate.execute(sqlBuilder.toString());
@@ -1064,26 +1070,34 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
registerColumnCodeMapping(codeMappings); registerColumnCodeMapping(codeMappings);
} }
if (changeColumns != null) { if (changeColumns != null) {
StringBuilder sqlBuilder = new StringBuilder("ALTER TABLE " + sqlGenerator.escape(datatableName)); final StringBuilder renameBuilder = new StringBuilder();
StringBuilder changeBuilder = new StringBuilder();
final StringBuilder constrainBuilder = new StringBuilder(); final StringBuilder constrainBuilder = new StringBuilder();
final Map<String, Long> codeMappings = new HashMap<>(); final Map<String, Long> codeMappings = new HashMap<>();
final List<String> removeMappings = new ArrayList<>(); final List<String> removeMappings = new ArrayList<>();
for (final JsonElement column : changeColumns) { for (final JsonElement column : changeColumns) {
// remove NULL values from column where mandatory is true // remove NULL values from column where mandatory is true
removeNullValuesFromStringColumn(datatableName, column.getAsJsonObject(), mapColumnNameDefinition); removeNullValuesFromStringColumn(datatableName, column.getAsJsonObject(), mapColumnNameDefinition);
parseDatatableColumnForUpdate(column.getAsJsonObject(), mapColumnNameDefinition, datatableName, renameBuilder,
parseDatatableColumnForUpdate(column.getAsJsonObject(), mapColumnNameDefinition, sqlBuilder, datatableName, changeBuilder, constrainBuilder, codeMappings, removeMappings, isConstraintApproach);
constrainBuilder, codeMappings, removeMappings, isConstraintApproach);
} }
// Remove the first comma, right after ALTER TABLE datatable // Remove the first comma, right after ALTER TABLE datatable
final int indexOfFirstComma = sqlBuilder.indexOf(","); StringBuilder sqlBuilder = renameBuilder;
if (indexOfFirstComma != -1) { if (!changeBuilder.isEmpty() || !constrainBuilder.isEmpty()) {
sqlBuilder = sqlBuilder.deleteCharAt(indexOfFirstComma); int idx = changeBuilder.indexOf(",");
if (idx > -1) {
changeBuilder.deleteCharAt(idx);
} else if ((idx = constrainBuilder.indexOf(",")) > -1) {
constrainBuilder.deleteCharAt(idx);
}
sqlBuilder.append("ALTER TABLE " + sqlGenerator.escape(datatableName)).append(changeBuilder).append(constrainBuilder);
} }
sqlBuilder.append(constrainBuilder);
try { try {
jdbcTemplate.execute(sqlBuilder.toString()); if (!sqlBuilder.isEmpty()) {
jdbcTemplate.execute(sqlBuilder.toString());
}
deleteColumnCodeMapping(removeMappings); deleteColumnCodeMapping(removeMappings);
registerColumnCodeMapping(codeMappings); registerColumnCodeMapping(codeMappings);
// update unique constraint // update unique constraint
@@ -1160,8 +1174,9 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
// check for unique constraint update // check for unique constraint update
String name = column.has(API_FIELD_NAME) ? column.get(API_FIELD_NAME).getAsString() : null; String name = column.has(API_FIELD_NAME) ? column.get(API_FIELD_NAME).getAsString() : null;
String columnNewName = column.has(API_FIELD_NEWNAME) ? column.get(API_FIELD_NEWNAME).getAsString() : null; String columnNewName = column.has(API_FIELD_NEWNAME) ? column.get(API_FIELD_NEWNAME).getAsString() : null;
final Boolean setUnique = column.has(API_FIELD_UNIQUE) ? column.get(API_FIELD_UNIQUE).getAsBoolean() : false;
final Boolean isAlreadyUnique = genericDataService.isExplicitlyUnique(datatableName, name); final boolean isAlreadyUnique = genericDataService.isExplicitlyUnique(datatableName, name);
boolean setUnique = column.has(API_FIELD_UNIQUE) ? column.get(API_FIELD_UNIQUE).getAsBoolean() : isAlreadyUnique;
String uniqueKeyName = datatableKeywordGenerator.generateUniqueKeyName(datatableName, name); String uniqueKeyName = datatableKeywordGenerator.generateUniqueKeyName(datatableName, name);
if (isAlreadyUnique) { if (isAlreadyUnique) {
@@ -1172,10 +1187,8 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
// if columnname changed // if columnname changed
checkColumnRenameAndModifyUniqueConstraint(datatableName, columnNewName, uniqueKeyName); checkColumnRenameAndModifyUniqueConstraint(datatableName, columnNewName, uniqueKeyName);
} }
} else { } else if (setUnique) {
if (setUnique) { checkColumnRenameAndCreateUniqueConstraint(datatableName, name, columnNewName, uniqueKeyName);
checkColumnRenameAndCreateUniqueConstraint(datatableName, name, columnNewName, uniqueKeyName);
}
} }
} }
@@ -1201,17 +1214,14 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
} }
private void createUniqueConstraint(String datatableName, String columnName, String uniqueKeyName) { private void createUniqueConstraint(String datatableName, String columnName, String uniqueKeyName) {
StringBuilder constrainBuilder = new StringBuilder(); String sql = "ALTER TABLE " + sqlGenerator.escape(datatableName) + " ADD CONSTRAINT " + sqlGenerator.escape(uniqueKeyName)
constrainBuilder.append("ALTER TABLE ").append(sqlGenerator.escape(datatableName)).append(" ADD CONSTRAINT ") + " UNIQUE (" + sqlGenerator.escape(columnName) + ");";
.append(sqlGenerator.escape(uniqueKeyName)).append(" UNIQUE (" + sqlGenerator.escape(columnName) + ");"); this.jdbcTemplate.execute(sql);
this.jdbcTemplate.execute(constrainBuilder.toString());
} }
private void dropUniqueConstraint(String datatableName, String uniqueKeyName) { private void dropUniqueConstraint(String datatableName, String uniqueKeyName) {
StringBuilder constrainBuilder = new StringBuilder(); String sql = "ALTER TABLE " + sqlGenerator.escape(datatableName) + " DROP CONSTRAINT " + sqlGenerator.escape(uniqueKeyName) + ";";
constrainBuilder.append("ALTER TABLE ").append(sqlGenerator.escape(datatableName)).append(" DROP CONSTRAINT ") this.jdbcTemplate.execute(sql);
.append(sqlGenerator.escape(uniqueKeyName)).append(";");
this.jdbcTemplate.execute(constrainBuilder.toString());
} }
private void updateIndexesForTable(String datatableName, JsonArray changeColumns, private void updateIndexesForTable(String datatableName, JsonArray changeColumns,
@@ -1230,24 +1240,24 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
private void updateIndexForColumn(String datatableName, JsonObject column, ResultsetColumnHeaderData columnMetaData) { private void updateIndexForColumn(String datatableName, JsonObject column, ResultsetColumnHeaderData columnMetaData) {
String name = column.has(API_FIELD_NAME) ? column.get(API_FIELD_NAME).getAsString() : null; String name = column.has(API_FIELD_NAME) ? column.get(API_FIELD_NAME).getAsString() : null;
String columnNewName = column.has(API_FIELD_NEWNAME) ? column.get(API_FIELD_NEWNAME).getAsString() : null; String columnNewName = column.has(API_FIELD_NEWNAME) ? column.get(API_FIELD_NEWNAME).getAsString() : null;
final Boolean setForUnique = column.has(API_FIELD_UNIQUE) ? column.get(API_FIELD_UNIQUE).getAsBoolean() : false; final boolean isAlreadyUnique = genericDataService.isExplicitlyUnique(datatableName, name);
final Boolean setForIndexed = column.has(API_FIELD_INDEXED) ? column.get(API_FIELD_INDEXED).getAsBoolean() : false; final boolean setForUnique = column.has(API_FIELD_UNIQUE) ? column.get(API_FIELD_UNIQUE).getAsBoolean() : isAlreadyUnique;
if (!setForUnique) { if (setForUnique) {
final Boolean isAlreadyIndexed = genericDataService.isExplicitlyIndexed(datatableName, name); return;
String indexName = datatableKeywordGenerator.generateIndexName(datatableName, name); }
if (isAlreadyIndexed) { final boolean isAlreadyIndexed = genericDataService.isExplicitlyIndexed(datatableName, name);
if (!setForIndexed) { boolean setForIndexed = column.has(API_FIELD_INDEXED) ? column.get(API_FIELD_INDEXED).getAsBoolean() : isAlreadyIndexed;
// drop index
dropIndex(datatableName, indexName);
} else { // if column name changed
checkColumnRenameAndModifyIndex(datatableName, columnNewName, indexName);
}
} else { String indexName = datatableKeywordGenerator.generateIndexName(datatableName, name);
if (setForIndexed) { if (isAlreadyIndexed) {
checkColumnRenameAndCreateIndex(datatableName, name, columnNewName, indexName); if (!setForIndexed) {
} // drop index
dropIndex(datatableName, indexName);
} else { // if column name changed
checkColumnRenameAndModifyIndex(datatableName, columnNewName, indexName);
} }
} else if (setForIndexed) {
checkColumnRenameAndCreateIndex(datatableName, name, columnNewName, indexName);
} }
} }
@@ -1297,7 +1307,7 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
validateDatatableRegistered(datatableName); validateDatatableRegistered(datatableName);
assertDataTableEmpty(datatableName); assertDataTableEmpty(datatableName);
deregisterDatatable(datatableName); deregisterDatatable(datatableName);
String[] sqlArray = null; String[] sqlArray;
if (this.configurationDomainService.isConstraintApproachEnabledForDatatables()) { if (this.configurationDomainService.isConstraintApproachEnabledForDatatables()) {
final String deleteColumnCodeSql = "delete from x_table_column_code_mappings where column_alias_name like'" final String deleteColumnCodeSql = "delete from x_table_column_code_mappings where column_alias_name like'"
+ datatableName.toLowerCase().replaceAll("\\s", "_") + "_%'"; + datatableName.toLowerCase().replaceAll("\\s", "_") + "_%'";
@@ -1600,7 +1610,6 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
private String getAddSql(final List<ResultsetColumnHeaderData> columnHeaders, final String datatable, final String fkName, private String getAddSql(final List<ResultsetColumnHeaderData> columnHeaders, final String datatable, final String fkName,
final Long appTableId, final Map<String, String> queryParams) { final Long appTableId, final Map<String, String> queryParams) {
final Map<String, Object> affectedColumns = getAffectedColumns(columnHeaders, queryParams, fkName); final Map<String, Object> affectedColumns = getAffectedColumns(columnHeaders, queryParams, fkName);
String pValueWrite = ""; String pValueWrite = "";
@@ -1626,12 +1635,10 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
} else { } else {
throw new IllegalStateException("Current database is not supported"); throw new IllegalStateException("Current database is not supported");
} }
} else { } else {
pValueWrite = singleQuote + this.genericDataService.replace(pValue, singleQuote, singleQuote + singleQuote) pValueWrite = singleQuote + this.genericDataService.replace(pValue, singleQuote, singleQuote + singleQuote)
+ singleQuote; + singleQuote;
} }
} }
columnName = sqlGenerator.escape(key); columnName = sqlGenerator.escape(key);
insertColumns += ", " + columnName; insertColumns += ", " + columnName;
@@ -1645,12 +1652,10 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
} }
} }
} }
addSql = "insert into " + sqlGenerator.escape(datatable) + " (" + sqlGenerator.escape(fkName) + " " + insertColumns + ")" addSql = "insert into " + sqlGenerator.escape(datatable) + " (" + sqlGenerator.escape(fkName) + " " + insertColumns + ")"
+ " select " + appTableId + " as id" + selectColumns; + " select " + appTableId + " as id" + selectColumns;
log.debug("{}", addSql); log.debug("{}", addSql);
return addSql; return addSql;
} }
@@ -1666,7 +1671,6 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
*/ */
public String getAddSqlWithScore(final List<ResultsetColumnHeaderData> columnHeaders, final String datatable, final String fkName, public String getAddSqlWithScore(final List<ResultsetColumnHeaderData> columnHeaders, final String datatable, final String fkName,
final Long appTableId, final Map<String, String> queryParams) { final Long appTableId, final Map<String, String> queryParams) {
final Map<String, Object> affectedColumns = getAffectedColumns(columnHeaders, queryParams, fkName); final Map<String, Object> affectedColumns = getAffectedColumns(columnHeaders, queryParams, fkName);
String pValueWrite = ""; String pValueWrite = "";
@@ -1700,13 +1704,11 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
+ " , ( SELECT SUM( code_score ) FROM m_code_value WHERE m_code_value.id IN (" + scoresId + " ) ) as score"; + " , ( SELECT SUM( code_score ) FROM m_code_value WHERE m_code_value.id IN (" + scoresId + " ) ) as score";
log.debug("{}", vaddSql); log.debug("{}", vaddSql);
return vaddSql; return vaddSql;
} }
private String getUpdateSql(List<ResultsetColumnHeaderData> columnHeaders, final String datatable, final String keyFieldName, private String getUpdateSql(List<ResultsetColumnHeaderData> columnHeaders, final String datatable, final String keyFieldName,
final Long keyFieldValue, final Map<String, Object> changedColumns) { final Long keyFieldValue, final Map<String, Object> changedColumns) {
// just updating fields that have changed since pre-update read - though // just updating fields that have changed since pre-update read - though
// its possible these values are different from the page the user was // its possible these values are different from the page the user was
// looking at and even different from the current db values (if some // looking at and even different from the current db values (if some
@@ -1765,7 +1767,6 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
} }
sql += " where " + keyFieldName + " = " + keyFieldValue; sql += " where " + keyFieldName + " = " + keyFieldValue;
return sql; return sql;
} }
@@ -1780,21 +1781,17 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
affectedAndChangedColumns.put(key, columnValue); affectedAndChangedColumns.put(key, columnValue);
} }
} }
return affectedAndChangedColumns; return affectedAndChangedColumns;
} }
private boolean columnChanged(final String key, final Object value, final GenericResultsetData grs) { private boolean columnChanged(final String key, final Object value, final GenericResultsetData grs) {
final List<Object> columnValues = grs.getData().get(0).getRow(); final List<Object> columnValues = grs.getData().get(0).getRow();
Object columnValue = null;
for (int i = 0; i < grs.getColumnHeaders().size(); i++) { for (int i = 0; i < grs.getColumnHeaders().size(); i++) {
if (key.equals(grs.getColumnHeaders().get(i).getColumnName())) { if (key.equals(grs.getColumnHeaders().get(i).getColumnName())) {
columnValue = columnValues.get(i); return notTheSame(columnValues.get(i), value);
return notTheSame(columnValue, value);
} }
} }
throw new PlatformDataIntegrityException("error.msg.invalid.columnName", "Parameter Column Name: " + key + " not found"); throw new PlatformDataIntegrityException("error.msg.invalid.columnName", "Parameter Column Name: " + key + " not found");
} }
@@ -1885,26 +1882,23 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
@Override @Override
public Long countDatatableEntries(final String datatableName, final Long appTableId, String foreignKeyColumn) { public Long countDatatableEntries(final String datatableName, final Long appTableId, String foreignKeyColumn) {
final String sqlString = "SELECT COUNT(" + sqlGenerator.escape(foreignKeyColumn) + ") FROM " + sqlGenerator.escape(datatableName) final String sqlString = "SELECT COUNT(" + sqlGenerator.escape(foreignKeyColumn) + ") FROM " + sqlGenerator.escape(datatableName)
+ " WHERE " + sqlGenerator.escape(foreignKeyColumn) + "=" + appTableId; + " WHERE " + sqlGenerator.escape(foreignKeyColumn) + " = " + appTableId;
final Long count = this.jdbcTemplate.queryForObject(sqlString, Long.class); // NOSONAR return this.jdbcTemplate.queryForObject(sqlString, Long.class); // NOSONAR
return count;
} }
public boolean isDatatableAttachedToEntityDatatableCheck(final String datatableName) { public boolean isDatatableAttachedToEntityDatatableCheck(final String datatableName) {
StringBuilder builder = new StringBuilder(); String sql = "SELECT COUNT(edc.x_registered_table_name) FROM x_registered_table xrt"
builder.append(" SELECT COUNT(edc.x_registered_table_name) FROM x_registered_table xrt"); + " JOIN m_entity_datatable_check edc ON edc.x_registered_table_name = xrt.registered_table_name"
builder.append(" JOIN m_entity_datatable_check edc ON edc.x_registered_table_name = xrt.registered_table_name"); + " WHERE edc.x_registered_table_name = '" + datatableName + "'";
builder.append(" WHERE edc.x_registered_table_name = '" + datatableName + "'"); final Long count = this.jdbcTemplate.queryForObject(sql, Long.class);
final Long count = this.jdbcTemplate.queryForObject(builder.toString(), Long.class);
return count > 0; return count > 0;
} }
// --- DbUtils --- // --- DbUtils ---
@NotNull @NotNull
private String mapApiTypeToDbType(@NotNull String apiType, Integer length) { private String mapApiTypeToDbType(String apiType, Integer length) {
if (StringUtils.isEmpty(apiType)) { if (StringUtils.isEmpty(apiType)) {
return ""; return "";
} }
@@ -231,11 +231,12 @@ public class DatatableHelper extends IntegrationTest {
public static List<HashMap<String, Object>> addDatatableColumns(List<HashMap<String, Object>> datatableColumnsList, String columnName, public static List<HashMap<String, Object>> addDatatableColumns(List<HashMap<String, Object>> datatableColumnsList, String columnName,
String columnType, boolean isMandatory, Integer length, String codeName) { String columnType, boolean isMandatory, Integer length, String codeName) {
final HashMap<String, Object> datatableColumnMap = new HashMap<>(); final HashMap<String, Object> datatableColumnMap = new HashMap<>();
datatableColumnMap.put("name", columnName); datatableColumnMap.put("name", columnName);
datatableColumnMap.put("type", columnType); if (columnType != null) {
datatableColumnMap.put("type", columnType);
}
datatableColumnMap.put("mandatory", isMandatory); datatableColumnMap.put("mandatory", isMandatory);
if (length != null) { if (length != null) {
datatableColumnMap.put("length", length); datatableColumnMap.put("length", length);
@@ -37,14 +37,17 @@ import java.text.DateFormat;
import java.text.ParseException; import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.apache.fineract.client.models.GetDataTablesResponse;
import org.apache.fineract.client.models.PostDataTablesAppTableIdResponse; import org.apache.fineract.client.models.PostDataTablesAppTableIdResponse;
import org.apache.fineract.client.models.PostDataTablesResponse; import org.apache.fineract.client.models.PostDataTablesResponse;
import org.apache.fineract.client.models.PutDataTablesAppTableIdDatatableIdResponse; import org.apache.fineract.client.models.PutDataTablesAppTableIdDatatableIdResponse;
import org.apache.fineract.client.models.PutDataTablesResponse; import org.apache.fineract.client.models.PutDataTablesResponse;
import org.apache.fineract.client.models.ResultsetColumnHeaderData;
import org.apache.fineract.client.util.Calls; import org.apache.fineract.client.util.Calls;
import org.apache.fineract.integrationtests.client.IntegrationTest; import org.apache.fineract.integrationtests.client.IntegrationTest;
import org.apache.fineract.integrationtests.common.ClientHelper; import org.apache.fineract.integrationtests.common.ClientHelper;
@@ -311,7 +314,6 @@ public class DatatableIntegrationTest extends IntegrationTest {
@Test @Test
public void validateCreateReadDeleteDatatableWithCaseSensitive() throws ParseException { public void validateCreateReadDeleteDatatableWithCaseSensitive() throws ParseException {
// creating datatable for client entity // creating datatable for client entity
final HashMap<String, Object> columnMap = new HashMap<>(); final HashMap<String, Object> columnMap = new HashMap<>();
final List<HashMap<String, Object>> datatableColumnsList = new ArrayList<>(); final List<HashMap<String, Object>> datatableColumnsList = new ArrayList<>();
@@ -546,7 +548,7 @@ public class DatatableIntegrationTest extends IntegrationTest {
final boolean genericResultSet = true; final boolean genericResultSet = true;
HashMap<String, Object> columnMap = new HashMap<>(); HashMap<String, Object> columnMap = new HashMap<>();
List<HashMap<String, Object>> datatableColumnsList = new ArrayList<>(); ArrayList<HashMap<String, Object>> datatableColumnsList = new ArrayList<>();
columnMap.put("datatableName", datatableName); columnMap.put("datatableName", datatableName);
columnMap.put("apptableName", CLIENT_APP_TABLE_NAME); columnMap.put("apptableName", CLIENT_APP_TABLE_NAME);
columnMap.put("entitySubType", CLIENT_PERSON_SUBTYPE_NAME); columnMap.put("entitySubType", CLIENT_PERSON_SUBTYPE_NAME);
@@ -562,10 +564,10 @@ public class DatatableIntegrationTest extends IntegrationTest {
DatatableHelper.verifyDatatableCreatedOnServer(this.requestSpec, this.responseSpec, datatableName); DatatableHelper.verifyDatatableCreatedOnServer(this.requestSpec, this.responseSpec, datatableName);
// Insert first values // Insert first values
final String value = Utils.randomStringGenerator("Q", 8); final String randomString = Utils.randomStringGenerator("Q", 8);
HashMap<String, Object> datatableEntryMap = new HashMap<>(); HashMap<String, Object> datatableEntryMap = new HashMap<>();
datatableEntryMap.put("itsANumber", randomNumber); datatableEntryMap.put("itsANumber", randomNumber);
datatableEntryMap.put("itsAString", value); datatableEntryMap.put("itsAString", randomString);
datatableEntryMap.put("locale", "en"); datatableEntryMap.put("locale", "en");
datatableEntryMap.put("dateFormat", "yyyy-MM-dd"); datatableEntryMap.put("dateFormat", "yyyy-MM-dd");
@@ -578,12 +580,14 @@ public class DatatableIntegrationTest extends IntegrationTest {
// Read the Datatable entry generated with genericResultSet in true (default) // Read the Datatable entry generated with genericResultSet in true (default)
HashMap<String, Object> items = this.datatableHelper.readDatatableEntry(datatableName, clientId, genericResultSet, null, ""); HashMap<String, Object> items = this.datatableHelper.readDatatableEntry(datatableName, clientId, genericResultSet, null, "");
assertNotNull(items); assertNotNull(items);
assertEquals(1, ((List) items.get("data")).size()); List data = (List) items.get("data");
LOG.info("Record created at {}", ((List) ((Map) ((List) items.get("data")).get(0)).get("row")).get(3)); assertEquals(1, data.size());
LOG.info("Record updated at {}", ((List) ((Map) ((List) items.get("data")).get(0)).get("row")).get(4)); List records = (List) ((Map) data.get(0)).get("row");
LOG.info("Record created at {}", records.get(3));
LOG.info("Record updated at {}", records.get(4));
assertEquals(clientId, ((List) ((Map) ((List) items.get("data")).get(0)).get("row")).get(0)); assertEquals(clientId, records.get(0));
assertEquals(value, ((List) ((Map) ((List) items.get("data")).get(0)).get("row")).get(2)); assertEquals(randomString, records.get(2));
// Update DataTable // Update DataTable
columnMap = new HashMap<>(); columnMap = new HashMap<>();
@@ -600,7 +604,7 @@ public class DatatableIntegrationTest extends IntegrationTest {
// Update DataTable Entry after Update DataTable schema // Update DataTable Entry after Update DataTable schema
datatableEntryMap = new HashMap<>(); datatableEntryMap = new HashMap<>();
final String textValue = Utils.randomStringGenerator(value, 120); final String textValue = Utils.randomStringGenerator(randomString, 120);
datatableEntryMap.put("itsAText", textValue); datatableEntryMap.put("itsAText", textValue);
datatableEntryMap.put("locale", "en"); datatableEntryMap.put("locale", "en");
datatableEntryMap.put("dateFormat", "yyyy-MM-dd"); datatableEntryMap.put("dateFormat", "yyyy-MM-dd");
@@ -615,13 +619,41 @@ public class DatatableIntegrationTest extends IntegrationTest {
// Read the Datatable entry generated with genericResultSet in true (default) // Read the Datatable entry generated with genericResultSet in true (default)
items = this.datatableHelper.readDatatableEntry(datatableName, clientId, genericResultSet, null, ""); items = this.datatableHelper.readDatatableEntry(datatableName, clientId, genericResultSet, null, "");
assertNotNull(items); assertNotNull(items);
assertEquals(1, ((List) items.get("data")).size()); data = (List) items.get("data");
LOG.info("Record created at {}", ((List) ((Map) ((List) items.get("data")).get(0)).get("row")).get(3)); assertEquals(1, data.size());
LOG.info("Record updated at {}", ((List) ((Map) ((List) items.get("data")).get(0)).get("row")).get(4));
assertEquals(clientId, ((List) ((Map) ((List) items.get("data")).get(0)).get("row")).get(0)); records = (List) ((Map) data.get(0)).get("row");
assertEquals(value, ((List) ((Map) ((List) items.get("data")).get(0)).get("row")).get(2)); LOG.info("Record created at {}", records.get(3));
assertEquals(textValue, ((List) ((Map) ((List) items.get("data")).get(0)).get("row")).get(5)); LOG.info("Record updated at {}", records.get(4));
assertEquals(clientId, records.get(0));
assertEquals(randomString, records.get(2));
assertEquals(textValue, records.get(5));
Integer resourceId = this.datatableHelper.deleteDatatableEntries(datatableName, clientId, "resourceId");
assertEquals(clientId, resourceId, "ERROR IN DELETING THE DATATABLE ENTRIES");
// Update - update, delete DataTable columns
columnMap = new HashMap<>();
columnMap.put("apptableName", CLIENT_APP_TABLE_NAME);
columnMap.put("entitySubType", CLIENT_PERSON_SUBTYPE_NAME);
List<Map<String, Object>> dropColumnsList = Collections.singletonList(Collections.singletonMap("name", "itsANumber"));
columnMap.put("dropColumns", dropColumnsList);
ArrayList<HashMap<String, Object>> changeColumnsList = new ArrayList<>();
DatatableHelper.addDatatableColumns(changeColumnsList, "itsAString", null, false, 100, null);
columnMap.put("changeColumns", changeColumnsList);
datatabelRequestJsonString = new Gson().toJson(columnMap);
LOG.info("map to update : {}", datatabelRequestJsonString);
datatableUpdateResponse = this.datatableHelper.updateDatatable(datatableName, datatabelRequestJsonString);
assertNotNull(datatableUpdateResponse);
assertEquals(datatableName, datatableUpdateResponse.getResourceIdentifier());
GetDataTablesResponse dataTable = datatableHelper.getDataTableDetails(datatableName);
List<ResultsetColumnHeaderData> columnHeaders = dataTable.getColumnHeaderData();
assertEquals(5, columnHeaders.size());
ResultsetColumnHeaderData stringColumn = columnHeaders.get(1);
assertEquals("itsAString", stringColumn.getColumnName());
assertEquals(100, stringColumn.getColumnLength());
} }
@Test @Test
@@ -176,6 +176,7 @@ public class DatatableUniqueAndIndexColumnTest {
changeColumns.setName(column1Name); changeColumns.setName(column1Name);
String newColumnName = column1Name + "new"; String newColumnName = column1Name + "new";
changeColumns.setNewName(newColumnName); changeColumns.setNewName(newColumnName);
changeColumns.setUnique(false);
changeColumns.setIndexed(true); changeColumns.setIndexed(true);
updateRequest.addChangeColumnsItem(changeColumns); updateRequest.addChangeColumnsItem(changeColumns);