我正在尝试在两个不同的数据库上进行更改日志: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中使用小写?
答案 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