Postgres 和 MS SQL 服务器上的相同更新日志
Same changelog on Postgres and MS SQL Server
我正在尝试使更新日志在两个不同的数据库上工作:MS SQL Server 和 PostgreSQL。变更日志在 SQL 服务器上运行良好,但数据库和字段的大小写使其在 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>
ERROR: relation "pushapplication" does not exist
table "PushApplication" 是在 PostgreSQL 中创建的,但是当它尝试创建 "PushVariant" 时,会为外键抛出此错误。
如果我将所有数据库名称和列名称更改为小写,这将起作用。但是,这会使 SQL 服务器的大小写不正确。
目标
目标是 "PushVariant" 在 SQL 服务器和 "pushvariant" 在 PostgreSQL。
有没有办法让变更日志中的大小写保留在 SQL 服务器中,但在 PostgreSQL 中保持小写?
使用通用源代码支持多个 DBMS 总是需要妥协。
即使有一个 SQL 标准明确定义了非引号标识符必须如何存储(全部大写),您所针对的两个 DBMS 都忽略了这一点。 Postgres 以小写形式存储未加引号的标识符,SQL 服务器是 "case-preserving"(尽管根据数据库的排序规则并不总是区分大小写 in)。
根据我在跨 DBMS 工作时的个人经验,(总是!)使用 un 引号和下划线的小写标识符是问题最少的方法。而且,如果您在某个时间点将 Oracle 加入其中,您就会将所有大写名称结束。
话虽如此:我认为 Liquibase 的引用策略实际上有一个错误。根据文档,objectQuotingStrategy="QUOTE_ONLY_RESERVED_WORDS"
应该 仅 引用保留字,因此在您的示例中不应引用任何内容。但是 Liquibase 仍然引用任何使用混合大小写的名称——当前 3.4.1 版本仍然如此。
我认为最好的事情是如果 Liquibase 支持选择 objectQuotingStrategy="NEVER"
(它不支持)
另一种选择是覆盖关于如何检测 Postgres 的 "need" 引用的默认实现,然后在 运行 针对 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("\""));
}
}
class 本质上只保留引用的标识符,只引用 确实 需要它的标识符。与内置 class 的最大区别在于它不检查混合大小写标识符。
如果将其放入 .jar 文件并放入 Liquibase 发行版的 lib
子文件夹中,它将被自动拾取。您需要做的就是添加参数 --databaseClass=NonQuotingPostgresDatabase
when 运行 Liquibase against Postgres
我正在尝试使更新日志在两个不同的数据库上工作:MS SQL Server 和 PostgreSQL。变更日志在 SQL 服务器上运行良好,但数据库和字段的大小写使其在 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>
ERROR: relation "pushapplication" does not exist
table "PushApplication" 是在 PostgreSQL 中创建的,但是当它尝试创建 "PushVariant" 时,会为外键抛出此错误。
如果我将所有数据库名称和列名称更改为小写,这将起作用。但是,这会使 SQL 服务器的大小写不正确。
目标
目标是 "PushVariant" 在 SQL 服务器和 "pushvariant" 在 PostgreSQL。
有没有办法让变更日志中的大小写保留在 SQL 服务器中,但在 PostgreSQL 中保持小写?
使用通用源代码支持多个 DBMS 总是需要妥协。
即使有一个 SQL 标准明确定义了非引号标识符必须如何存储(全部大写),您所针对的两个 DBMS 都忽略了这一点。 Postgres 以小写形式存储未加引号的标识符,SQL 服务器是 "case-preserving"(尽管根据数据库的排序规则并不总是区分大小写 in)。
根据我在跨 DBMS 工作时的个人经验,(总是!)使用 un 引号和下划线的小写标识符是问题最少的方法。而且,如果您在某个时间点将 Oracle 加入其中,您就会将所有大写名称结束。
话虽如此:我认为 Liquibase 的引用策略实际上有一个错误。根据文档,objectQuotingStrategy="QUOTE_ONLY_RESERVED_WORDS"
应该 仅 引用保留字,因此在您的示例中不应引用任何内容。但是 Liquibase 仍然引用任何使用混合大小写的名称——当前 3.4.1 版本仍然如此。
我认为最好的事情是如果 Liquibase 支持选择 objectQuotingStrategy="NEVER"
(它不支持)
另一种选择是覆盖关于如何检测 Postgres 的 "need" 引用的默认实现,然后在 运行 针对 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("\""));
}
}
class 本质上只保留引用的标识符,只引用 确实 需要它的标识符。与内置 class 的最大区别在于它不检查混合大小写标识符。
如果将其放入 .jar 文件并放入 Liquibase 发行版的 lib
子文件夹中,它将被自动拾取。您需要做的就是添加参数 --databaseClass=NonQuotingPostgresDatabase
when 运行 Liquibase against Postgres