Postgres和MS SQL Server上的相同更改日志

时间:2015-08-18 12:06:08

标签: sql-server postgresql liquibase

我正在尝试在两个不同的数据库上进行更改日志: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>
  

错误:关系&#34; pushapplication&#34;不存在

表&#34; PushApplication&#34;在PostgreSQL中创建但是当它尝试创建&#34; PushVariant&#34;时,会抛出外键的错误。

如果我将所有数据库名称和列名称更改为小写,这将有效。但是,这会使SQL Server出现错误的情况。

目标

目标是让#V; PushVariant&#34;在SQL Server和&#34; pushvariant&#34;在PostgreSQL中。

是否有一种方法可以将更改日志中的案例保留在SQL Server中但在PostgreSQL中使用小写?

1 个答案:

答案 0 :(得分:1)

使用通用源代码支持多个DBMS始终是妥协。

即使有一个SQL标准明确定义了如何存储非引用标识符(所有大写),但您要定位的两个DBMS都忽略了这一点。 Postgres以小写形式存储未引用的标识符,SQL Server是“保留大小写”(虽然并非总是 - 敏感 - 取决于数据库的整理)。

根据我在进行跨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