Skip to content

Commit

Permalink
chunk long strings on insert for Oracle (DAT-16774) (#5566)
Browse files Browse the repository at this point in the history
  • Loading branch information
StevenMassaro committed Feb 12, 2024
1 parent 4dcf165 commit 76b9373
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package liquibase.datatype.core

import liquibase.Scope
import liquibase.command.util.CommandUtil
import liquibase.extension.testing.testsystem.DatabaseTestSystem
import liquibase.extension.testing.testsystem.TestSystemFactory
import liquibase.extension.testing.testsystem.spock.LiquibaseIntegrationTest
import spock.lang.Shared
import spock.lang.Specification

import java.sql.ResultSet

@LiquibaseIntegrationTest
class OracleCharTypeIntegrationTest extends Specification {

@Shared
private DatabaseTestSystem oracle = Scope.currentScope.getSingleton(TestSystemFactory).getTestSystem("oracle") as DatabaseTestSystem

def "insert statement with value longer than 4000 characters is chunked and inserted into clob column properly"() {
when:
CommandUtil.runUpdate(oracle, "src/test/resources/changelogs/common/large-clob-data-load.xml")

then:
ResultSet resultSet = oracle.getConnection().prepareStatement("SELECT id, name FROM person").executeQuery()
resultSet.next()
resultSet.getInt(1) == 1
resultSet.getString(2) == """Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut sagittis feugiat velit tempus sollicitudin. Ut dapibus, tellus vel commodo commodo, est felis varius felis, eget pulvinar mauris risus eu lacus. Vestibulum quis orci vitae sem pulvinar semper. Fusce sit amet nulla enim. Maecenas faucibus vehicula pharetra. Pellentesque semper lacinia semper. Integer placerat ipsum a neque dapibus, vel eleifend ligula fermentum. Mauris hendrerit iaculis euismod. In venenatis ligula in velit venenatis, eu euismod lorem congue. Integer metus tortor, porttitor ut urna ut, maximus venenatis quam. Nam dictum odio libero, in feugiat lorem convallis vel. Phasellus enim lorem, ullamcorper eu pellentesque et, aliquam quis libero. Pellentesque bibendum felis vitae pulvinar tincidunt. Quisque et nulla convallis, varius lorem vel, iaculis justo. Nulla facilisi.Cras sagittis convallis risus et eleifend. Nunc ut pretium ipsum. Sed nec hendrerit sem. Morbi vestibulum eros vehicula erat mattis fermentum. Vivamus vel maximus felis. Nunc eget mauris in risus elementum ullamcorper. Phasellus semper consectetur mollis. Vivamus sit amet arcu vitae ligula molestie sollicitudin non sed ante. Suspendisse et rhoncus est.Nam vitae varius eros. Donec lorem dolor, varius eget fringilla quis, hendrerit quis sapien. Donec eget ipsum tempus, auctor augue nec, pharetra felis. Praesent maximus iaculis nunc, et iaculis magna pellentesque a. Nullam efficitur arcu dui, tempor luctus justo aliquam id. Morbi mattis, mi pellentesque fringilla cursus, urna mauris malesuada est, id sodales massa urna id massa. Aliquam elementum lobortis ligula, at sodales ante suscipit et. Aenean pellentesque finibus augue sit amet pulvinar. Duis nec nibh facilisis, fringilla libero ut, dignissim mauris. Donec orci arcu, vulputate tristique diam at, porttitor vulputate purus.Donec tincidunt eleifend arcu a pretium. Nunc nec turpis laoreet, luctus ex eu, pretium lacus. Mauris mi sapien, egestas a arcu sed, sodales imperdiet libero. Aliquam erat volutpat. Nulla eu est eleifend, elementum nisi non, dignissim arcu. In posuere eu sapien a facilisis. Nullam nec dui lectus. Praesent dictum augue sapien, vitae gravida justo mattis sed. Donec ullamcorper ullamcorper convallis. Integer dapibus maximus augue, sed luctus elit cursus et.Fusce ac augue nec orci faucibus bibendum. In vestibulum sodales porta. Praesent iaculis consectetur suscipit. Phasellus tempor est quis aliquet varius. Aliquam tincidunt vel urna ac fermentum. In mattis massa nibh, eu vestibulum turpis volutpat nec. Phasellus posuere leo ultrices purus fringilla fringilla. Integer augue ligula, varius quis elit a, sagittis egestas turpis. Curabitur velit metus, blandit vel nunc et, faucibus varius lorem. Pellentesque pharetra lacus justo, nec finibus ipsum tristique sit amet. Phasellus purus turpis, accumsan sit amet dolor quis, vehicula varius metus. Praesent sed nunc libero. Morbi faucibus iaculis nisi, at lobortis risus commodo et.Suspendisse potenti. Sed leo risus, venenatis at condimentum quis, facilisis pulvinar sem. Aenean varius libero nisl, et volutpat neque facilisis nec. Maecenas ac mi sit amet purus imperdiet condimentum. Vestibulum eget lacus odio. Nullam suscipit orci id ligula sollicitudin convallis. Morbi pulvinar, erat imperdiet auctor iaculis, massa nulla luctus erat, ac vehicula velit est eu dolor. Morbi lectus justo, vestibulum a purus nec, porttitor imperdiet magna. Nunc ut aliquet dui, at suscipit augue.Nam congue augue arcu, sit amet vehicula elit tincidunt in. Nunc sed purus lectus. Donec ultricies at diam eget consectetur. Nunc et odio nulla. Donec sed lacus diam. Vivamus at ultrices risus, vitae pretium lorem. Curabitur bibendum, nulla non bibendum ullamcorper, lectus ipsum viverra urna, a condimentum mauris arcu id felis.Proin mattis rutrum accumsan. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Integer gravida arcu vel commodo maximus. Interdum et malesuada fames ac ante ipsum primis in faucibus. Cras aliquet enim lacus, eu commodo erat lobortis ut. Vivamus bibendum mi ut commodo egestas. Maecenas blandit id risus vel suscipit. Donec pellentesque in lacus eget fringilla. Cras et tortor magna. In mattis eros nulla, vel porttitor massa congue quis. Phasellus quis elit erat. Vestibulum at sagittis dui.Curabitur eleifend consequat justo, nec luctus turpis accumsan vel. Nullam et ultrices libero. Etiam nibh ante, egestas sit amet iaculis nec, rutrum id risus. Integer vitae consectetur nibh, ut lacinia eros. Cras pellentesque euismod neque. Donec leo lacus, eleifend eget erat vitae, dignissim pulvinar est. Curabitur nulla nisl, faucibus feugiat pulvinar vitae, laoreet in dolor.Quisque leo erat, feugiat vel sodales eget, commodo eu lorem. Etiam sit amet laoreet ligula. Fusce lacus neque, porta in orci id, aliquam placerat magna. Mauris a semper mi. Aliquam augue sapien, tempor ut sollicitudin eu, consectetur ac urna. Nulla odio metus, fringilla in ultrices a, dapibus nec ligula. Nulla sed accumsan felis. Curabitur lacinia arcu molestie fringilla dictum. Fusce id neque nibh. Vestibulum vitae diam efficitur, hendrerit enim lacinia, auctor nibh.Sed pulvinar mattis velit, sit amet aliquam ante dictum et. Fusce tristique aliquet velit. Nunc varius quam non eros efficitur, eget maximus leo ultrices. Sed interdum velit dolor, non cursus lectus condimentum quis. Donec nec finibus est. Donec viverra et est eu ultrices. Nam tempor, odio a venenatis ornare, nulla arcu sodales felis, vitae sollicitudin ex ipsum sed risus. Aliquam convallis sed mauris id mattis. Praesent justo felis, semper vel lacus vestibulum, convallis accumsan orci. Sed a dictum dui, ut ultrices tortor. Ut sit amet dolor sit amet justo vulputate ornare.Sed justo libero, bibendum a nisl lacinia, porttitor accumsan dui. Vestibulum auctor dapibus gravida. Vestibulum rhoncus lacinia mauris vel fringilla. Aenean pellentesque feugiat risus, nec pellentesque orci facilisis sit amet. Vivamus malesuada, enim non imperdiet volutpat, tortor ipsum porttitor nisi, vitae dignissim augue lorem id felis. Sed eleifend diam magna, sit amet dictum orci suscipit quis. Sed ante turpis, gravida a diam id, efficitur pretium eros. Sed id vestibulum dolor, eget sollicitudin purus. Vivamus maximus, ipsum ultricies rhoncus pretium, quam risus malesuada massa, rutrum rhoncus purus lectus eget justo. Vestibulum elementum nunc venenatis, rutrum ligula at, consectetur ligula. Sed non arcu ut arcu lacinia blandit. Quisque quis eros et urna semper sodales. Curabitur et auctor ante, mollis hendrerit ex. Praesent eu aliquam ipsum, vel eleifend risus. Vestibulum facilisis sollicitudin lectus sit amet tristique. Vestibulum molestie nibh in maximus ultrices. Nam semper urna ut rhoncus tristique. Sed accumsan turpis quis."""
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>

<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">

<changeSet id="1" author="your.name" labels="example-label" context="example-context">
<createTable tableName="person">
<column name="id" type="int" autoIncrement="true">
<constraints primaryKey="true" nullable="false"/>
</column>
<column name="name" type="clob">
<constraints nullable="false"/>
</column>
</createTable>
</changeSet>

<changeSet id="2" author="your.name" labels="example-label" context="example-context">
<insert tableName="person">
<column name="id" value="1" />
<column name="name" value="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut sagittis feugiat velit tempus sollicitudin. Ut dapibus, tellus vel commodo commodo, est felis varius felis, eget pulvinar mauris risus eu lacus. Vestibulum quis orci vitae sem pulvinar semper. Fusce sit amet nulla enim. Maecenas faucibus vehicula pharetra. Pellentesque semper lacinia semper. Integer placerat ipsum a neque dapibus, vel eleifend ligula fermentum. Mauris hendrerit iaculis euismod. In venenatis ligula in velit venenatis, eu euismod lorem congue. Integer metus tortor, porttitor ut urna ut, maximus venenatis quam. Nam dictum odio libero, in feugiat lorem convallis vel. Phasellus enim lorem, ullamcorper eu pellentesque et, aliquam quis libero. Pellentesque bibendum felis vitae pulvinar tincidunt. Quisque et nulla convallis, varius lorem vel, iaculis justo. Nulla facilisi.Cras sagittis convallis risus et eleifend. Nunc ut pretium ipsum. Sed nec hendrerit sem. Morbi vestibulum eros vehicula erat mattis fermentum. Vivamus vel maximus felis. Nunc eget mauris in risus elementum ullamcorper. Phasellus semper consectetur mollis. Vivamus sit amet arcu vitae ligula molestie sollicitudin non sed ante. Suspendisse et rhoncus est.Nam vitae varius eros. Donec lorem dolor, varius eget fringilla quis, hendrerit quis sapien. Donec eget ipsum tempus, auctor augue nec, pharetra felis. Praesent maximus iaculis nunc, et iaculis magna pellentesque a. Nullam efficitur arcu dui, tempor luctus justo aliquam id. Morbi mattis, mi pellentesque fringilla cursus, urna mauris malesuada est, id sodales massa urna id massa. Aliquam elementum lobortis ligula, at sodales ante suscipit et. Aenean pellentesque finibus augue sit amet pulvinar. Duis nec nibh facilisis, fringilla libero ut, dignissim mauris. Donec orci arcu, vulputate tristique diam at, porttitor vulputate purus.Donec tincidunt eleifend arcu a pretium. Nunc nec turpis laoreet, luctus ex eu, pretium lacus. Mauris mi sapien, egestas a arcu sed, sodales imperdiet libero. Aliquam erat volutpat. Nulla eu est eleifend, elementum nisi non, dignissim arcu. In posuere eu sapien a facilisis. Nullam nec dui lectus. Praesent dictum augue sapien, vitae gravida justo mattis sed. Donec ullamcorper ullamcorper convallis. Integer dapibus maximus augue, sed luctus elit cursus et.Fusce ac augue nec orci faucibus bibendum. In vestibulum sodales porta. Praesent iaculis consectetur suscipit. Phasellus tempor est quis aliquet varius. Aliquam tincidunt vel urna ac fermentum. In mattis massa nibh, eu vestibulum turpis volutpat nec. Phasellus posuere leo ultrices purus fringilla fringilla. Integer augue ligula, varius quis elit a, sagittis egestas turpis. Curabitur velit metus, blandit vel nunc et, faucibus varius lorem. Pellentesque pharetra lacus justo, nec finibus ipsum tristique sit amet. Phasellus purus turpis, accumsan sit amet dolor quis, vehicula varius metus. Praesent sed nunc libero. Morbi faucibus iaculis nisi, at lobortis risus commodo et.Suspendisse potenti. Sed leo risus, venenatis at condimentum quis, facilisis pulvinar sem. Aenean varius libero nisl, et volutpat neque facilisis nec. Maecenas ac mi sit amet purus imperdiet condimentum. Vestibulum eget lacus odio. Nullam suscipit orci id ligula sollicitudin convallis. Morbi pulvinar, erat imperdiet auctor iaculis, massa nulla luctus erat, ac vehicula velit est eu dolor. Morbi lectus justo, vestibulum a purus nec, porttitor imperdiet magna. Nunc ut aliquet dui, at suscipit augue.Nam congue augue arcu, sit amet vehicula elit tincidunt in. Nunc sed purus lectus. Donec ultricies at diam eget consectetur. Nunc et odio nulla. Donec sed lacus diam. Vivamus at ultrices risus, vitae pretium lorem. Curabitur bibendum, nulla non bibendum ullamcorper, lectus ipsum viverra urna, a condimentum mauris arcu id felis.Proin mattis rutrum accumsan. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Integer gravida arcu vel commodo maximus. Interdum et malesuada fames ac ante ipsum primis in faucibus. Cras aliquet enim lacus, eu commodo erat lobortis ut. Vivamus bibendum mi ut commodo egestas. Maecenas blandit id risus vel suscipit. Donec pellentesque in lacus eget fringilla. Cras et tortor magna. In mattis eros nulla, vel porttitor massa congue quis. Phasellus quis elit erat. Vestibulum at sagittis dui.Curabitur eleifend consequat justo, nec luctus turpis accumsan vel. Nullam et ultrices libero. Etiam nibh ante, egestas sit amet iaculis nec, rutrum id risus. Integer vitae consectetur nibh, ut lacinia eros. Cras pellentesque euismod neque. Donec leo lacus, eleifend eget erat vitae, dignissim pulvinar est. Curabitur nulla nisl, faucibus feugiat pulvinar vitae, laoreet in dolor.Quisque leo erat, feugiat vel sodales eget, commodo eu lorem. Etiam sit amet laoreet ligula. Fusce lacus neque, porta in orci id, aliquam placerat magna. Mauris a semper mi. Aliquam augue sapien, tempor ut sollicitudin eu, consectetur ac urna. Nulla odio metus, fringilla in ultrices a, dapibus nec ligula. Nulla sed accumsan felis. Curabitur lacinia arcu molestie fringilla dictum. Fusce id neque nibh. Vestibulum vitae diam efficitur, hendrerit enim lacinia, auctor nibh.Sed pulvinar mattis velit, sit amet aliquam ante dictum et. Fusce tristique aliquet velit. Nunc varius quam non eros efficitur, eget maximus leo ultrices. Sed interdum velit dolor, non cursus lectus condimentum quis. Donec nec finibus est. Donec viverra et est eu ultrices. Nam tempor, odio a venenatis ornare, nulla arcu sodales felis, vitae sollicitudin ex ipsum sed risus. Aliquam convallis sed mauris id mattis. Praesent justo felis, semper vel lacus vestibulum, convallis accumsan orci. Sed a dictum dui, ut ultrices tortor. Ut sit amet dolor sit amet justo vulputate ornare.Sed justo libero, bibendum a nisl lacinia, porttitor accumsan dui. Vestibulum auctor dapibus gravida. Vestibulum rhoncus lacinia mauris vel fringilla. Aenean pellentesque feugiat risus, nec pellentesque orci facilisis sit amet. Vivamus malesuada, enim non imperdiet volutpat, tortor ipsum porttitor nisi, vitae dignissim augue lorem id felis. Sed eleifend diam magna, sit amet dictum orci suscipit quis. Sed ante turpis, gravida a diam id, efficitur pretium eros. Sed id vestibulum dolor, eget sollicitudin purus. Vivamus maximus, ipsum ultricies rhoncus pretium, quam risus malesuada massa, rutrum rhoncus purus lectus eget justo. Vestibulum elementum nunc venenatis, rutrum ligula at, consectetur ligula. Sed non arcu ut arcu lacinia blandit. Quisque quis eros et urna semper sodales. Curabitur et auctor ante, mollis hendrerit ex. Praesent eu aliquam ipsum, vel eleifend risus. Vestibulum facilisis sollicitudin lectus sit amet tristique. Vestibulum molestie nibh in maximus ultrices. Nam semper urna ut rhoncus tristique. Sed accumsan turpis quis." />
</insert>
</changeSet>
</databaseChangeLog>
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
package liquibase.datatype.core;

import liquibase.Scope;
import liquibase.change.core.LoadDataChange;
import liquibase.database.Database;
import liquibase.database.core.H2Database;
import liquibase.database.core.MSSQLDatabase;
import liquibase.database.core.OracleDatabase;
import liquibase.database.core.PostgresDatabase;
import liquibase.datatype.DataTypeInfo;
import liquibase.datatype.DatabaseDataType;
import liquibase.datatype.LiquibaseDataType;
import liquibase.integration.commandline.LiquibaseCommandLineConfiguration;
import liquibase.statement.DatabaseFunction;
import liquibase.util.StringUtil;

import java.math.BigInteger;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;

@DataTypeInfo(name="char", aliases = {"java.sql.Types.CHAR", "bpchar", "character"}, minParameters = 0, maxParameters = 1, priority = LiquibaseDataType.PRIORITY_DEFAULT)
Expand Down Expand Up @@ -70,16 +74,34 @@ public String objectToSql(Object value, Database database) {
if ((value == null) || "null".equals(value.toString().toLowerCase(Locale.US))) {
return null;
}
String stringValue = value.toString();

if (value instanceof DatabaseFunction) {
return value.toString();
return stringValue;
}

String val = String.valueOf(value);
if ((database instanceof MSSQLDatabase) && !StringUtil.isAscii(val)) {
return "N'"+database.escapeStringForDatabase(val)+"'";
}

/*
It is a somewhat safe assumption that if the database is Oracle and the length of the string exceeds 4000
characters then the column must be a clob type column, because Oracle doesn't support varchars longer than
2000 characters. It would be better to read the column config directly, but that info isn't available at this
point in the code.
*/
if (database instanceof OracleDatabase &&
LiquibaseCommandLineConfiguration.WORKAROUND_ORACLE_CLOB_CHARACTER_LIMIT.getCurrentValue() &&
stringValue.length() > 4000) {
Scope.getCurrentScope().getLog(getClass()).fine("A string longer than 4000 characters has been detected on an insert statement, " +
"and the database is Oracle. Oracle forbids insert statements with strings longer than 4000 characters, " +
"so Liquibase is going to workaround this limitation. If an error occurs, this can be disabled by setting "
+ LiquibaseCommandLineConfiguration.WORKAROUND_ORACLE_CLOB_CHARACTER_LIMIT.getKey() + " to false.");
List<String> chunks = StringUtil.splitToChunks(stringValue, 4000);
return "to_clob( '" + StringUtil.join(chunks, "' ) || to_clob( '", obj -> database.escapeStringForDatabase(obj.toString())) + "' )";
}

return "'"+database.escapeStringForDatabase(val)+"'";
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public class LiquibaseCommandLineConfiguration implements AutoloadedConfiguratio
public static final ConfigurationDefinition<Boolean> ADD_EMPTY_MDC_VALUES;
public static final ConfigurationDefinition<Boolean> SHOW_HIDDEN_ARGS;
public static final ConfigurationDefinition<Boolean> INCLUDE_MATCHING_TAG_IN_ROLLBACK_OLDEST;
public static final ConfigurationDefinition<Boolean> WORKAROUND_ORACLE_CLOB_CHARACTER_LIMIT;

static {
ConfigurationDefinition.Builder builder = new ConfigurationDefinition.Builder("liquibase");
Expand Down Expand Up @@ -134,6 +135,12 @@ public class LiquibaseCommandLineConfiguration implements AutoloadedConfiguratio
.setDefaultValue(true)
.setHidden(true)
.build();

WORKAROUND_ORACLE_CLOB_CHARACTER_LIMIT = builder.define("workaroundOracleClobCharacterLimit", Boolean.class)
.setDescription("If true, long strings in Oracle will be chunked at 4000 characters when an insert statement is run, to avoid running afoul of Oracle's 4000 character limit for insert statements to clob type columns (which appears as 'ORA-01704: string literal too long.')")
.setDefaultValue(true)
.setHidden(true)
.build();
}

public interface ArgumentConverter {
Expand Down

0 comments on commit 76b9373

Please sign in to comment.