liquibase / liquibase

Main Liquibase Source
https://www.liquibase.org
Apache License 2.0
4.66k stars 1.85k forks source link

SQL_CHECK_POSTGRES_SEQUENCE_EXISTS in serialized xml changeset #5633

Open evilbc opened 7 months ago

evilbc commented 7 months ago

Search first

Description

When serializing ChangeSet with XMLChangeLogSerializer if the ChangeSet has SequenceExistsPrecondition, the serialized xml has an invalid SQL_CHECK_POSTGRES_SEQUENCE_EXISTS field

Example xml:

<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
    <changeSet author="author" id="id" runInTransaction="false">
        <preConditions onError="HALT" onFail="HALT" onSqlOutput="IGNORE">
            <sequenceExists SQL_CHECK_POSTGRES_SEQUENCE_EXISTS="SELECT c.relname FROM pg_class c JOIN pg_namespace ns on c.relnamespace = ns.oid WHERE c.relkind = 'S' AND ns.nspname = ? and c.relname = ?" sequenceName="test2"/>
        </preConditions>
        <createSequence sequenceName="test"/>
    </changeSet>
</databaseChangeLog>

It seems to be caused by AbstractLiquibaseSerializable#getSerializableFields - it uses reflection to get all fields for serialization, but SequenceExistsPrecondition has a field that should not be serialized

Steps To Reproduce

ChangeSet changeSet = new ChangeSet("id", "author", false, false, null, null, null, false, null, null);
CreateSequenceChange change = new CreateSequenceChange();
change.setSequenceName("test");
changeSet.addChange(change);

PreconditionContainer container = new PreconditionContainer();
SequenceExistsPrecondition precondition = new SequenceExistsPrecondition();
precondition.setSequenceName("test2");
container.addNestedPrecondition(precondition);
changeSet.setPreconditions(container);

XMLChangeLogSerializer serializer = new XMLChangeLogSerializer();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
serializer.write(Collections.singletonList(changeSet), baos);
String serialized = baos.toString();
System.out.println(serialized);

Expected/Desired Behavior

SQL_CHECK_POSTGRES_SEQUENCE_EXISTS should not appear

Liquibase Version

4.24.0

Database Vendor & Version

No response

Liquibase Integration

maven

Liquibase Extensions

liquibase-core

OS and/or Infrastructure Type/Provider

Windows 10

Additional Context

No response

Are you willing to submit a PR?

tati-qalified commented 5 months ago

Hi @evilbc, thank you for reporting this issue.

I have been able to replicate it with the XML, YML and JSON serializers. I've also found other errors when generating the changelogs programmatically.

Using your same code, here are the results I got:

XML

<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
    <changeSet author="author" id="id" runInTransaction="false">
        <preConditions onError="HALT" onFail="HALT" onSqlOutput="IGNORE">
            <sequenceExists SQL_CHECK_POSTGRES_SEQUENCE_EXISTS="SELECT c.relname FROM pg_class c JOIN pg_namespace ns on c.relnamespace = ns.oid WHERE c.relkind = 'S' AND ns.nspname = ? and c.relname = ?" sequenceName="test2"/>
        </preConditions>
        <createSequence sequenceName="test"/>
    </changeSet>
</databaseChangeLog>

YML

databaseChangeLog:
- changeSet:
    id: id
    author: author
    preconditions:
      preConditions:
        nestedPreconditions:
        - sequenceExists:
            SQL_CHECK_POSTGRES_SEQUENCE_EXISTS: SELECT c.relname FROM pg_class c JOIN
              pg_namespace ns on c.relnamespace = ns.oid WHERE c.relkind = 'S' AND
              ns.nspname = ? and c.relname = ?
            sequenceName: test2
        onError: !!liquibase.precondition.core.PreconditionContainer$ErrorOption 'HALT'
        onFail: !!liquibase.precondition.core.PreconditionContainer$FailOption 'HALT'
        onSqlOutput: !!liquibase.precondition.core.PreconditionContainer$OnSqlOutputOption 'IGNORE'
    runInTransaction: false
    changes:
    - createSequence:
        sequenceName: test

Note the onError, onFail and onSqlOutput attributes, and that preConditions is written twice.

JSON

{
  "databaseChangeLog": [
    {
      "changeSet": {
        "id": "id",
        "author": "author",
        "preconditions": {
          "preConditions": {
            "nestedPreconditions": [
              {
                "sequenceExists": {
                  "SQL_CHECK_POSTGRES_SEQUENCE_EXISTS": "SELECT c.relname FROM pg_class c JOIN pg_namespace ns on c.relnamespace = ns.oid WHERE c.relkind = 'S' AND ns.nspname = ? and c.relname = ?",
                  "sequenceName": "test2"
                }
              }
            ],
            "onError": "HALT",
            "onFail": "HALT",
            "onSqlOutput": "IGNORE"
          }
        },
        "runInTransaction": false,
        "changes": [
          {
            "createSequence": {
              "sequenceName": "test"
            }
          }
        ]
      }
    }
  ]
}

Note the same errors as the YML format.


I'll be leaving this issue open for the community to propose a fix for. Our development team will be available to provide guidance whenever necessary.

Thank you, Tatiana