class LoadUpdateDataChange extends LoadDataChange {
    private String primaryKey;
    private Boolean onlyUpdate = Boolean.FALSE;

    @Override
    protected boolean hasPreparedStatementsImplemented() { return false; }

    @Override
    @DatabaseChangeProperty(description = "Name of the table to insert or update data in", requiredForDatabase = ALL)
    public String getTableName() {
        return super.getTableName();
    }

    public void setPrimaryKey(String primaryKey) {
        this.primaryKey = primaryKey;
    }

    @DatabaseChangeProperty(description = "Comma-delimited list of columns for the primary key",
            requiredForDatabase = ALL)
    public String getPrimaryKey() {
        return primaryKey;
    }

    @DatabaseChangeProperty(description = "If true, records with no matching database record should be ignored",
            since = "3.3", supportsDatabase = ALL)
    public Boolean getOnlyUpdate() {
        if (onlyUpdate == null) {
            return false;
        }
        return onlyUpdate;
    }

    public void setOnlyUpdate(Boolean onlyUpdate) {
        this.onlyUpdate = ((onlyUpdate == null) ? Boolean.FALSE : onlyUpdate);
    }

    /**
     * Creates a {@link liquibase.statement.core.InsertOrUpdateStatement} statement object for the specified table
     * @param catalogName name of the catalog where the table exists
     * @param schemaName name of the schema where the table exists
     * @param tableName the table name to insert/update data
     * @return a specialised {@link liquibase.statement.core.InsertOrUpdateStatement} that will either insert or update rows in the target table
     */
    @Override
    protected InsertStatement createStatement(String catalogName, String schemaName, String tableName) {
        return new InsertOrUpdateStatement(catalogName, schemaName, tableName, this.primaryKey, this.getOnlyUpdate());
    }

    @Override
    protected ExecutablePreparedStatementBase createPreparedStatement(
            Database database, String catalogName, String schemaName, String tableName,
            List<LoadDataColumnConfig> columns, ChangeSet changeSet, ResourceAccessor resourceAccessor) {
        // TODO: Not supported yet. When this is implemented, we can remove hasPreparedStatementsImplemented().
        throw new UnsupportedOperationException("Executable Prepared Statements are not supported for " +
                "LoadUpdateDataChange yet. Very sorry.");
    }

    @Override
    public SqlStatement[] generateRollbackStatements(Database database) throws RollbackImpossibleException {
        List<SqlStatement> statements = new ArrayList<>();
        List<SqlStatement> finalForwardList = new ArrayList<>();

        // If we are dealing with a batched UPDATE, "unroll" the individual statements first.
        for (SqlStatement thisForward : this.generateStatements(database)) {
            if (thisForward instanceof BatchDmlExecutablePreparedStatement) {
                finalForwardList.addAll(
                        ((BatchDmlExecutablePreparedStatement)thisForward).getIndividualStatements()
                );
            } else {
                finalForwardList.add(thisForward);
            }
        }

        for (SqlStatement thisForward : finalForwardList) {
            InsertOrUpdateStatement thisInsert = (InsertOrUpdateStatement) thisForward;
            DeleteStatement delete = new DeleteStatement(getCatalogName(), getSchemaName(), getTableName());
            delete.setWhere(getWhere(thisInsert, database));
            statements.add(delete);
        }

        return statements.toArray(SqlStatement.EMPTY_SQL_STATEMENT);
    }

    private String getWhere(InsertOrUpdateStatement insertOrUpdateStatement, Database database) {<FILL_FUNCTION_BODY>}

    @Override
    public String getSerializedObjectNamespace() {
        return STANDARD_CHANGELOG_NAMESPACE;
    }

    @Override
    public ChangeStatus checkStatus(Database database) {
        return new ChangeStatus().unknown("Cannot check loadUpdateData status");
    }
}

        StringBuilder where = new StringBuilder();

        String[] pkColumns = insertOrUpdateStatement.getPrimaryKey().split(",");

        for (String thisPkColumn : pkColumns) {
            Object newValue = insertOrUpdateStatement.getColumnValues().get(thisPkColumn);
            where.append(database.escapeColumnName(insertOrUpdateStatement.getCatalogName(),
                    insertOrUpdateStatement.getSchemaName(),
                    insertOrUpdateStatement.getTableName(),
                    thisPkColumn)).append(((newValue == null) || StringUtil.equalsWordNull(newValue.toString())) ? " is " : " = ");

            if ((newValue == null) || StringUtil.equalsWordNull(newValue.toString())) {
                where.append("NULL");
            } else {
                where.append(DataTypeFactory.getInstance().fromObject(newValue, database).objectToSql(newValue,
                        database));
            }

            where.append(" AND ");
        }

        where.delete(where.lastIndexOf(" AND "), where.lastIndexOf(" AND ") + " AND ".length());
        return where.toString();
    
<methods>public non-sealed void <init>() ,public void addColumn(liquibase.change.core.LoadDataColumnConfig) ,public liquibase.change.ChangeStatus checkStatus(liquibase.database.Database) ,public liquibase.change.CheckSum generateCheckSum() ,public boolean generateRollbackStatementsVolatile(liquibase.database.Database) ,public liquibase.statement.SqlStatement[] generateStatements(liquibase.database.Database) ,public boolean generateStatementsVolatile(liquibase.database.Database) ,public liquibase.util.csv.CSVReader getCSVReader() throws java.io.IOException, liquibase.exception.LiquibaseException,public List<liquibase.change.core.LoadDataColumnConfig> getColumns() ,public java.lang.String getCommentLineStartsWith() ,public java.lang.String getConfirmationMessage() ,public java.lang.String getEncoding() ,public java.lang.String getFile() ,public java.lang.String getQuotchar() ,public java.lang.String getSeparator() ,public java.lang.String getSerializedObjectNamespace() ,public java.lang.String getTableName() ,public java.lang.Boolean getUsePreparedStatements() ,public java.lang.Boolean isRelativeToChangelogFile() ,public void setColumns(List<liquibase.change.core.LoadDataColumnConfig>) ,public void setCommentLineStartsWith(java.lang.String) ,public void setEncoding(java.lang.String) ,public void setFile(java.lang.String) ,public void setQuotchar(java.lang.String) ,public void setRelativeToChangelogFile(java.lang.Boolean) ,public void setSeparator(java.lang.String) ,public void setUsePreparedStatements(java.lang.Boolean) ,public boolean supports(liquibase.database.Database) ,public liquibase.exception.ValidationErrors validate(liquibase.database.Database) ,public void validateColumn(liquibase.change.core.LoadDataColumnConfig, liquibase.exception.ValidationErrors, java.lang.String) ,public liquibase.exception.Warnings warn(liquibase.database.Database) <variables>public static final java.util.regex.Pattern BASE64_PATTERN,public static final java.lang.String DEFAULT_COMMENT_PATTERN,private static final liquibase.logging.Logger LOG,private List<liquibase.change.core.LoadDataColumnConfig> columns,private java.lang.String commentLineStartsWith,private static final java.util.ResourceBundle coreBundle,private java.lang.String encoding,private java.lang.String file,private java.lang.String quotchar,private java.lang.Boolean relativeToChangelogFile,private java.lang.String separator,private java.lang.Boolean usePreparedStatements