【问题标题】:Same changelog on Postgres and MS SQL ServerPostgres 和 MS SQL Server 上的相同变更日志
【发布时间】:2015-11-11 08:44:30
【问题描述】:

我正在尝试在两个不同的数据库上制作变更日志:MS SQL Server 和 PostgreSQL。更改日志在 SQL Server 上运行良好,但数据库和字段的大小写使其在 PostgreSQL 上中断。我尝试过不在值周围使用引号(这会引发错误)并使用了objectQuotingStategy="QUOTE_ALL_OBJECTS",但两者都不起作用。

<?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-3.1.xsd">

    <changeSet id="create-PushApplication" author="me">
        <createTable tableName="PushApplication">
            <column name="ApplicationKey" type="NUMERIC(19,0)" autoIncrement="true" remarks="Unique id of the application">
                <constraints primaryKey="true" primaryKeyName="PushApplication_PK" nullable="false" />
            </column>
            <column name="ApplicationId" type="VARCHAR(100)" remarks="Id of the application in the Push Server">
                <constraints unique="true" uniqueConstraintName="PushApplication_U1" nullable="false" />
            </column>
            <column name="MasterSecret" type="VARCHAR(100)" remarks="Password for the application in the Push Server">
                <constraints nullable="false" />
            </column>
            <column name="Name" type="VARCHAR(100)" remarks="Name of the application">
                <constraints nullable="false" />
            </column>
            <column name="Description" type="VARCHAR(200)" remarks="Description of the application" />
            <column name="DateCreated" type="DATETIME" remarks="Date the application was created" />
        </createTable>
    </changeSet>

    <changeSet id="create-PushVariant" author="me">
        <createTable tableName="PushVariant">
            <column name="VariantKey" type="NUMERIC(19,0)" autoIncrement="true" remarks="Unique id of the variant">
                <constraints primaryKey="true" primaryKeyName="PushVariant_PK" nullable="false" />
            </column>
            <column name="VariantId" type="VARCHAR(100)" remarks="Id of the variant in the Push Server">
                <constraints unique="true" uniqueConstraintName="PushVariant_UK1" nullable="false" />
            </column>
            <column name="Secret" type="VARCHAR(100)" remarks="Password for the variant in the Push Server">
                <constraints nullable="false" />
            </column>
            <column name="Name" type="VARCHAR(100)" remarks="Name of the variant">
                <constraints nullable="false" />
            </column>
            <column name="Description" type="VARCHAR(200)" remarks="Description of the variant" />
            <column name="ApplicationKey" type="NUMERIC(19,0)" remarks="Id of the application the variant belogns to">
                <constraints nullable="false" foreignKeyName="PushVariant_FK1" references="PushApplication(ApplicationKey)" />
            </column>
        </createTable>
    </changeSet>

</databaseChangeLog>

错误:关系“pushapplication”不存在

在 PostgreSQL 中创建了表“PushApplication”,但是当它尝试创建“PushVariant”时,会为外键抛出此错误。

如果我将所有数据库名称和列名称更改为小写,这将起作用。但是,这会使 SQL Server 的大小写不正确。

目标

目标是在 SQL Server 中拥有“PushVariant”,在 PostgreSQL 中拥有“pushvariant”。

有没有办法让变更日志中的大小写保留在 SQL Server 中,但在 PostgreSQL 中小写?

【问题讨论】:

  • "但是,这会使 SQL Server 的大小写不正确" 小写名称的“不正确”是什么?如果您想在不同的 DBMS 之间保持兼容,您将必须做出一些妥协。无论如何,我个人更喜欢push_variant 而不是PushVariant。与 SQL 标准相比,SQL Server 保持大小写不变的行为与 Postgres 以小写形式编写所有内容的行为一样错误。 SQL 标准要求所有未加引号的标识符都以大写形式存储。
  • @a_horse_with_no_name 使用我们多年的当前架构,我们在 SQL Server 中不使用小写名称。如果它们变成小写,它将与当前设置不匹配。根据您的最后一条评论,我应该将所有内容全部大写吗?
  • 啊,好吧。如果我真的想保留这个案例,那么几乎可以创建不同的变更日志。如果我不这样做,请考虑使用下划线。谢谢!如果可以的话,请根据您的 cmets 的要点创建一个答案,我会接受您的答案:-)
  • 我为我的答案添加了另一个解决方案。默认行为实际上可以很容易地更改。

标签: sql-server postgresql liquibase


【解决方案1】:

使用通用源代码支持多个 DBMS 总是需要妥协。

尽管有一个 SQL 标准明确定义了非引号标识符的存储方式(全部大写),但您所针对的两个 DBMS 都忽略了这一点。 Postgres 以小写形式存储未引用的标识符,SQL Server 是“保留大小写”的(尽管根据数据库的排序规则,并不总是区分大小写 in)。

根据我在进行跨 DBMS 工作时的个人经验,(总是!)使用带有下划线的 un 引用的小写标识符是问题最少的方法。而且,如果您在某个时间点将 Oracle 混入其中,您最终会得到所有大写名称。


话虽如此:我认为 Liquibase 的引用策略实际上有一个错误。根据文档,objectQuotingStrategy="QUOTE_ONLY_RESERVED_WORDS" 应该引用保留字,因此在您的示例中不应引用任何内容。但是 Liquibase 仍然引用使用混合大小写的任何名称 - 当前 3.4.1 版本仍然是这种情况。

我认为最好的事情是如果 Liquibase 支持选择 objectQuotingStrategy="NEVER"(它不支持)


另一种选择是覆盖默认实现,即如何检测 Postgres 引用的“需要”,然后在针对 Postgres 数据库运行时使用该实现。

实现实际上很短,只需要检查非标准名称(以数字开头,包含空格或其他非法字符),然后引用那些。

我刚刚尝试了以下实现:

public class NonQuotingPostgresDatabase
  extends PostgresDatabase {

    @Override
    public String correctObjectName(String objectName, Class<? extends DatabaseObject> objectType) {
        return quoteIfNecessary(objectName);
    }

    @Override
    public String escapeObjectName(String objectName, Class<? extends DatabaseObject> objectType) {
        return quoteIfNecessary(objectName);
    }

    private String quoteIfNecessary(String objectName) {
        if (requiresQuoting(objectName)) {
            return "\"" + objectName + "\"";
        }
        return objectName;

    }
    protected boolean requiresQuoting(String identifier) {
        if (identifier == null) {
            return false;
        }
        if (isQuoted(identifier)) {
            return false;
        }
        return (identifier.contains("-") || startsWithNumeric(identifier) || isReservedWord(identifier));
    }

    protected boolean isQuoted(String identifier) {
        if (identifier == null) {
            return false;
        }
        return (identifier.startsWith("\"") && identifier.endsWith("\""));
    }

}

该类本质上只保留了带引号的标识符,并且只引用了真正需要它的标识符。内置类的最大区别在于它不检查混合大小写标识符。

如果将其放入 .jar 文件并放入 Liquibase 发行版的 lib 子文件夹,它将被自动拾取。您需要做的就是在针对 Postgres 运行 Liquibase 时添加参数 --databaseClass=NonQuotingPostgresDatabase

【讨论】:

  • 我仍在尝试解决这个问题。我将它添加到 JAR 中并将其放在 lib 文件夹中,但它总是抛出 ClassNotFoundException。希望我能尽快得到它
  • @Ascalonian:如果您将类放入一个包中(例如 com.ascalonia),那么您将该包名包含在参数中:---databaseClass=com.ascalonia.NonQuotingPostgresDatabase。另外:您是否使用提供的批处理文件来启动 Liquibase?还是您使用其他方法?
  • 我确实把它放在一个包里,然后就这样试了。我正在使用提供的 liquibase.jar 文件:java -jar liquibase.jar --driver=org.postgresql.Driver --changeLogFile=com/example/db/changelog/db.changelog-01.xml --url=jdbc:postgresql://localhost:5432/database --username=postgres --password=postgres --databaseClass=com.ascalonian.liquibase.NonQuotingPostgresDatabase update
  • @Ascalonian:使用java -jar .. 将忽略任何其他库。您需要通过提供的批处理文件或指定完整的类路径 java -cp liquibase.jar;yourlib.jar liquibase.integration.commandline.Main ... 来运行它
猜你喜欢
  • 1970-01-01
  • 2023-03-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多