Hi, I have encountered an odd and very ridiculous issue. We were deploying a PL/SQL procedure from the .sql file via Liquibase 4.8 Docker build, and Liquibase broke the .sql code during the deployment.
One of the lines in said procedure was:
DBMS_OUTPUT.put_line ( ’ ---------------EMPTY-----------------’ );
And Liquibase transformed it into this:
DBMS_OUTPUT.put_line ( ’
And by leaving only the one quote, then breaking the entire procedure. What I don’t understand, is, why Liquibase should transfer anything inside the quotes, as it’s a simple tring, where anyone should be able to write anything.
Liquibase 4.8 - Docker Build used by CICD pipeline through Gitlab
Oracle 19c database
(unfortunately we are not able to use any higher version, because, and that is another ridiculous issue, when using endDelimiter:/ as I feel, it’s an industry standard when deploying SQL and PL/SQL changes, whenever this character is used in a mathematical operation (divide), it’s being recognized as an endDelimiter.
It’s documented here, and I find it absurd that no-one has looked into this critical and absurd issue since March.
opened 04:00PM - 27 Mar 22 UTC
DBOracle
OSLinux
ver4.9.0
<!--- This environment context section helps us quickly review your PR.
… Please take a minute to fill-out this information. -->
## Environment
**Liquibase Version**:
4.9.0
**Liquibase Integration & Version**: <Pick one: CLI, maven, gradle, spring boot, servlet, etc.>
CLI in Image from https://hub.docker.com/r/liquibase/liquibase
**Liquibase Extension(s) & Version**:
n.a.
**Database Vendor & Version**:
Oracle Database 21 XE (21.3.0.0) provided by [gvenzl/oracle-xe
](https://hub.docker.com/layers/oracle-xe/gvenzl/oracle-xe/21.3.0-slim/images/sha256-d496c24007fe6c828826f7a74702733275140097758500f986a8531fb4ec754b?context=explore)
**Operating System Type & Version**:
Podman 4.0.1 on Linux
## Description
With Liquibase **4.8.0** the installation of an PL/SQL function in an Oracle 21 XE database including **/** for calculation purposes in the source code is working without any problems.
After upgrade of the Liquibase image to version **4.9.0**, the installed source code of the function in the database ends before **/** in the return statement and the function in the dabase is invalid because of missing code.
## Steps To Reproduce
### Environment
- [Oracle Database 21 XE Image](https://hub.docker.com/layers/oracle-xe/gvenzl/oracle-xe/21.3.0-slim/images/sha256-d496c24007fe6c828826f7a74702733275140097758500f986a8531fb4ec754b?context=explore)
- JDBC driver from Oracle Instant Client 21.5.0.0.0 64bit on Linux
- [Liquibase 4.8.0 Image](https://hub.docker.com/layers/liquibase/liquibase/liquibase/4.8.0/images/sha256-ee265e6b60249a831a512bdcd068857c8bef70a6490016676c931e9462b66a99?context=explore)
- [Liquibase 4.9.0 Image](https://hub.docker.com/layers/liquibase/liquibase/liquibase/latest/images/sha256-6405a2f22d815351785adeb32e23b24918e86e483e27e4b936678c2ddf3c244a?context=explore)
- Podman 4.0.1
### Changelog install_functions.yaml
```
#
# install_functions.yaml
#
databaseChangeLog:
- include:
file: create_fnc_get_elapsed_time.sql
relativeToChangelogFile: true
```
### Changeset create_fnc_get_elapsed_time.sql
```
--liquibase formatted sql
--changeset data_public:create_fnc_get_elapsed_time.sql dbms:oracle endDelimiter:/ failOnError:true runAlways:true runOnChange:true stripComments:false splitStatements:true
CREATE OR REPLACE FUNCTION fnc_get_elapsed_time
(i_start_time IN NUMBER
,i_end_time IN NUMBER
)
RETURN NUMBER
PARALLEL_ENABLE
IS
BEGIN
RETURN ((i_end_time - i_start_time) / 100);
EXCEPTION
WHEN OTHERS
THEN
BEGIN
RAISE;
END;
END fnc_get_elapsed_time;
/
```
### Execution of Liquibase 4.8.0
```
time podman run \
--name lb-ora \
--rm \
--volume /database:/liquibase/changelog:Z \
--volume /opt/oracle/instantclient:/liquibase/instantclient:ro \
docker.io/liquibase/liquibase:4.8 \
--changeLogFile=install_functions.yaml \
--defaultsFile=/liquibase-development.properties \
--password="pwd" \
update
```
### Execution of Liquibase 4.9.0
```
time podman run \
--name lb-ora \
--rm \
--volume /database:/liquibase/changelog:Z \
--volume /opt/oracle/instantclient:/liquibase/instantclient:ro \
docker.io/liquibase/liquibase:latest \
--changeLogFile=install_functions.yaml \
--defaultsFile=/liquibase-development.properties \
--password="pwd" \
update
```
### Liquibase properties
```
#
# properties for installation of database objects using Liquibase
#
classpath: /liquibase/changelog:/liquibase/instantclient
driver: oracle.jdbc.OracleDriver
liquibase.hub.mode: off
logLevel: warning
url: jdbc:oracle:thin:@<hostname>:1521/xepdb1
username: <username>
strict: true
```
## Actual Behavior
Partly output when Liquibase **4.9.0** is executed with **logLevel: debug**:
```
[2022-03-27 15:52:48] FINE [liquibase.executor] Executing with the 'jdbc' executor
[2022-03-27 15:52:48] FINE [liquibase.util] Computed checksum for inputStream as 31c679bed1defccaefd71f030edcc363
[2022-03-27 15:52:48] FINE [liquibase.util] Computed checksum for 8:31c679bed1defccaefd71f030edcc363: as 94140ee341f64935c5e26926f93ff590
[2022-03-27 15:52:48] FINE [liquibase.servicelocator] Loaded liquibase.hub.HubService instance liquibase.hub.core.MockHubService
[2022-03-27 15:52:48] FINE [liquibase.servicelocator] Loaded liquibase.hub.HubService instance liquibase.hub.core.StandardHubService
Running Changeset: 012-DATA_PUBLIC/create_fnc_get_elapsed_time.sql::create_fnc_get_elapsed_time.sql::data_public
[2022-03-27 15:52:48] FINE [liquibase.changelog] Running Changeset: 012-DATA_PUBLIC/create_fnc_get_elapsed_time.sql::create_fnc_get_elapsed_time.sql::data_public
[2022-03-27 15:52:48] FINE [liquibase.executor] Changeset 012-DATA_PUBLIC/create_fnc_get_elapsed_time.sql::create_fnc_get_elapsed_time.sql::data_public
[2022-03-27 15:52:48] FINE [liquibase.changelog] Reading ChangeSet: 012-DATA_PUBLIC/create_fnc_get_elapsed_time.sql::create_fnc_get_elapsed_time.sql::data_public
[2022-03-27 15:52:48] FINE [liquibase.database] Executing Statement: CREATE OR REPLACE FUNCTION fnc_get_elapsed_time
/**
* Calculates the current elapsed time in seconds since the given start time.
*
*
*/
(i_start_time IN NUMBER
,i_end_time IN NUMBER
)
RETURN NUMBER
PARALLEL_ENABLE
IS
BEGIN
RETURN ((i_end_time - i_start_time)
[2022-03-27 15:52:48] FINE [liquibase.executor] Executing with the 'jdbc' executor
[2022-03-27 15:52:48] FINE [liquibase.configuration] No configuration value for liquibase.sql.logLevel found
[2022-03-27 15:52:48] FINE [liquibase.configuration] Configuration liquibase.sql.logLevel is using the default value of FINE
[2022-03-27 15:52:48] FINE [liquibase.executor] CREATE OR REPLACE FUNCTION fnc_get_elapsed_time
/**
* Calculates the current elapsed time in seconds since the given start time.
*
*
*/
(i_start_time IN NUMBER
,i_end_time IN NUMBER
)
RETURN NUMBER
PARALLEL_ENABLE
IS
BEGIN
RETURN ((i_end_time - i_start_time)
[2022-03-27 15:52:48] FINE [liquibase.executor] 0 row(s) affected
[2022-03-27 15:52:48] FINE [liquibase.database] Executing Statement: 100);
EXCEPTION
WHEN OTHERS
THEN
BEGIN
RAISE;
END;
END fnc_get_elapsed_time;
[2022-03-27 15:52:48] FINE [liquibase.executor] Executing with the 'jdbc' executor
[2022-03-27 15:52:48] FINE [liquibase.executor] 100);
EXCEPTION
WHEN OTHERS
THEN
BEGIN
RAISE;
END;
END fnc_get_elapsed_time;
[2022-03-27 15:52:48] FINE [liquibase.executor] Release Database Lock
[2022-03-27 15:52:48] FINE [liquibase.executor] Executing with the 'jdbc' executor
[2022-03-27 15:52:48] INFO [liquibase.lockservice] Successfully released change log lock
[2022-03-27 15:52:48] SEVERE [liquibase.integration] ORA-00900: invalid SQL statement
liquibase.exception.CommandExecutionException: liquibase.exception.LiquibaseException: Unexpected error running Liquibase: Migration failed for change set 012-DATA_PUBLIC/create_fnc_get_elapsed_time.sql::create_fnc_get_elapsed_time.sql::data_public:
Reason: liquibase.exception.DatabaseException: ORA-00900: invalid SQL statement
[Failed SQL: (900) 100);
EXCEPTION
WHEN OTHERS
THEN
BEGIN
RAISE;
END;
END fnc_get_elapsed_time;]
at liquibase.command.CommandScope.execute(CommandScope.java:163)
at liquibase.integration.commandline.CommandRunner.call(CommandRunner.java:51)
at liquibase.integration.commandline.CommandRunner.call(CommandRunner.java:21)
at picocli.CommandLine.executeUserObject(CommandLine.java:1953)
at picocli.CommandLine.access$1300(CommandLine.java:145)
at picocli.CommandLine$RunLast.executeUserObjectOfLastSubcommandWithSameParent(CommandLine.java:2352)
at picocli.CommandLine$RunLast.handle(CommandLine.java:2346)
at picocli.CommandLine$RunLast.handle(CommandLine.java:2311)
at picocli.CommandLine$AbstractParseResultHandler.execute(CommandLine.java:2179)
at picocli.CommandLine.execute(CommandLine.java:2078)
at liquibase.integration.commandline.LiquibaseCommandLine.lambda$execute$1(LiquibaseCommandLine.java:325)
at liquibase.Scope.child(Scope.java:189)
at liquibase.Scope.child(Scope.java:165)
at liquibase.integration.commandline.LiquibaseCommandLine.execute(LiquibaseCommandLine.java:291)
at liquibase.integration.commandline.LiquibaseCommandLine.main(LiquibaseCommandLine.java:80)
Caused by: liquibase.exception.LiquibaseException: Unexpected error running Liquibase: Migration failed for change set 012-DATA_PUBLIC/create_fnc_get_elapsed_time.sql::create_fnc_get_elapsed_time.sql::data_public:
Reason: liquibase.exception.DatabaseException: ORA-00900: invalid SQL statement
[Failed SQL: (900) 100);
EXCEPTION
WHEN OTHERS
THEN
BEGIN
RAISE;
END;
END fnc_get_elapsed_time;]
at liquibase.integration.commandline.Main$1.run(Main.java:443)
at liquibase.integration.commandline.Main$1.run(Main.java:221)
at liquibase.Scope.child(Scope.java:189)
at liquibase.Scope.child(Scope.java:165)
at liquibase.integration.commandline.Main.run(Main.java:221)
at liquibase.command.AbstractCliWrapperCommandStep.run(AbstractCliWrapperCommandStep.java:32)
at liquibase.command.CommandScope.execute(CommandScope.java:157)
... 14 more
Caused by: liquibase.exception.LiquibaseException: liquibase.exception.MigrationFailedException: Migration failed for change set 012-DATA_PUBLIC/create_fnc_get_elapsed_time.sql::create_fnc_get_elapsed_time.sql::data_public:
Reason: liquibase.exception.DatabaseException: ORA-00900: invalid SQL statement
```
## Expected/Desired Behavior
PL/SQL source code using **/** for calculations is valid installed in an Oracle database and is not recognized as "endDelimiter" character of a Liquibase changeset.
## Screenshots (if appropriate)
PL/SQL source code in Oracle SQL Developer after installation with Liquibase **4.8.0**:
![grafik](https://user-images.githubusercontent.com/100380779/160288940-43eaef59-1488-4c2c-b32a-7e5ed42d0de1.png)
PL/SQL source code in Oracle SQL Developer after installation with Liquibase **4.9.0**:
![grafik](https://user-images.githubusercontent.com/100380779/160288886-bee791ae-2794-4953-bc0c-dedfb23249a0.png)
## Additional Context
There seems to be a change in [liquibase-core/src/main/java/liquibase/util/StringUtil.java](https://github.com/liquibase/liquibase/commit/b949f0af7c8aa5478ab14b1659e04da1c35e844d), which could be relevant for the changed behaviour in Liquibase **4.9.0**.
So sorry, if I am overly agressive, but this tool had already caused our team SOOO much headaches, and has issues with so many small things SQLPlus and SQLDeveloper never had…
Can you provide your changeset parameters? You can choose any character you want as the end delimiter.
I ran this changeset as a test, with your dbms_output line, and it ran fine, no errors.
<changeSet id="create_procedurev1" author="XYZ" runOnChange="true">
<sql endDelimiter="/">
create or replace procedure test_proc is
v_dummy1 date;
v_dummy2 date;
begin
--This is select 1
select sysdate into v_dummy1 from "DUAL";
--This is select 2
select sysdate-1 into v_dummy2 from dual;
DBMS_OUTPUT.put_line ( '---------------EMPTY-----------------' );
end test_proc;
/
</sql>
<rollback/>
</changeSet>
We are using .sql files, and there are the metadata which are in every .sql file Liquibase deploys.
–liquibase formatted sql
–changeset author:datalite endDelimiter:/ runOnChange:true
And we are using includeAll tag which deploys every .sql file in the target folder.
Your changeset definition is essentially the same as mine, which I just ran without error, the procedure was created as expected:
--liquibase formatted sql
--changeset XYZ:pl-sql-example runOnChange:true endDelimiter:/ stripComments:false
create or replace procedure test_proc2 is
v_dummy1 date;
v_dummy2 date;
begin
--This is select 1
select sysdate into v_dummy1 from "DUAL";
--This is select 2
select sysdate-1 into v_dummy2 from dual;
DBMS_OUTPUT.put_line ( '---------------EMPTY-----------------' );
end test_proc2;
/
--rollback not required
Running Changeset: db-changelog-10.sql::pl-sql-example::XYZ
[2022-06-24 19:00:22] INFO [liquibase.changelog] Custom SQL executed
[2022-06-24 19:00:22] INFO [liquibase.changelog] ChangeSet db-changelog-10.sql::pl-sql-example::XYZ ran successfully in 100ms
You see I also have two comments lines in my code, with double-dashes style, which cause no issues.
Can you check your quotes in the dbms_output statement to make sure they are not formatted as some alternate microsoft quote? I had to edit and fix them in the code that I cut from your example above.
Also, if your code contains a “/”, then choose another delimiter, like “$” or “GO”. Any delimiter will work as long as it is not in your code.
Unfortunately, it was truly the dashes and that was the only thing I changed in that text. After that, it worked as expected. I haven’t touched the quotes at all. I literally just changed dashes to equal signs.
DBMS_OUTPUT.put_line ( ‘=============EMPTY===========’ );
That is very strange, glad you got it to work. I will say that I’m using 4.9.1 and I’m not using the docker image, so I’m not sure what difference that is making. But I’ve been using the same pl/sql techniques going back to v3.6 without any issues with dashes.
Yeah, it’s very strange indeed, I had trouble understanding why that could be a problem.