FederatedUserAttributeTextColumnMigration.java
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed 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.keycloak.connections.jpa.updater.liquibase.custom;
import liquibase.database.core.MySQLDatabase;
import liquibase.exception.CustomChangeException;
import liquibase.statement.core.RawParameterizedSqlStatement;
import org.keycloak.storage.jpa.JpaHashUtils;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
/**
* The MySQL database is the only database where columns longer than 255 characters are changed to a TEXT column, allowing
* for up to 64k characters. See {@link org.keycloak.connections.jpa.updater.liquibase.MySQL8VarcharType} for the implementation.
* As the new code expects all information longer than 2024 characters in the new column, this migration copies over the values.
*
* @author Alexander Schwartz
*/
public class FederatedUserAttributeTextColumnMigration extends CustomKeycloakTask {
@Override
protected void generateStatementsImpl() throws CustomChangeException {
if (database instanceof MySQLDatabase) {
try (PreparedStatement ps = connection.prepareStatement("SELECT t.ID, t.VALUE" +
" FROM " + getTableName("FED_USER_ATTRIBUTE") + " t" +
" WHERE LENGTH(t.VALUE) > 2024");
ResultSet resultSet = ps.executeQuery()
) {
while (resultSet.next()) {
String id = resultSet.getString(1);
String value = resultSet.getString(2);
// The SQL LENGTH() will count bytes, where Java's length() will count Unicode characters.
// There's also SQL CHAR_LENGTH() which is probably equivalent to Java's Character.codePointCount(),
// but it is not a fit here as we're not using code points in the JPA entities
if (value.length() > 2024) {
statements.add(new RawParameterizedSqlStatement("UPDATE " + getTableName("FED_USER_ATTRIBUTE") + " SET VALUE = null, LONG_VALUE_HASH = ?, LONG_VALUE_HASH_LOWER_CASE = ?, LONG_VALUE = ? WHERE ID = ?",
JpaHashUtils.hashForAttributeValue(value),
JpaHashUtils.hashForAttributeValueLowerCase(value),
value,
id));
}
}
} catch (Exception e) {
throw new CustomChangeException(getTaskId() + ": Exception when updating data from previous version", e);
}
}
}
@Override
protected String getTaskId() {
return "Leave only single offline session per user and client";
}
}