2015-08-18 61 views
1

上相同的changelog我努力讓自己在兩個不同的數據庫更新日誌的工作:MS SQL Server和PostgreSQL的。更新日誌在SQL Server上正常工作,但數據庫和字段的情況使它在PostgreSQL上中斷。我試過不使用引號(引發錯誤)並使用objectQuotingStategy="QUOTE_ALL_OBJECTS",這兩個都不起作用。的Postgres和MS SQL Server

<?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

表「PushApplication」 PostgreSQL裏創建,但是當它試圖創建「PushVariant」,這個錯誤被拋出外鍵。如果我改變所有的數據庫名和列名,以小寫

這將工作。但是,這會使SQL Server具有不正確的情況。

目標

的目標是有 「PushVariant」 在SQL Server和PostgreSQL中 「pushvariant」。

有沒有一種辦法,在更改日誌的情況下可以保持在SQL Server中,但是有它在PostgreSQL的小寫?

+1

「*然而,這使得SQL Server有不正確的情況*」什麼是不正確的小寫字母名稱?如果你想跨越不同的DBMS兼容,你會**有一些妥協。無論如何,我個人比'PushVariant'更喜歡'push_variant'。與SQL標準相比,SQL Server保持大小寫的行爲與Postgres以小寫形式寫入所有內容的行爲相比是錯誤的。 SQL標準要求所有未加引號的標識符以大寫形式存儲。 –

+0

@a_horse_with_no_name對於我們已經存在多年的當前模式,我們不使用SQL Server中的小寫名稱。如果它們變成小寫,它將不符合當前的設置。有了你最後的評論,我應該把所有東西都寫成大寫嗎? – Ascalonian

+0

啊,好吧。所以,如果我真的想保留案例,創建不同的更新日誌。如果我不這樣做,請使用下劃線查看。謝謝!如果你可以請你的評論中的關鍵點創建一個答案,我會接受你的答案:-) – Ascalonian

回答

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子文件夾中,它將被自動拾取。所有你需要做的就是添加參數--databaseClass=NonQuotingPostgresDatabase當運行Liquibase針對Postgres

+0

我仍然試圖擺弄這個。我將它添加到JAR中並將其放在lib文件夾中,但它總是拋出ClassNotFoundException異常。希望我能儘早得到它 – Ascalonian

+0

@Ascalonian:如果你把類放入一個包(例如com.ascalonia),那麼你可以用參數包括packagename:'--- databaseClass = com.ascalonia.NonQuotingPostgresDatabase'。另外:您是否使用提供的批處理文件來啓動Liquibase?或者你使用不同的方法? –

+0

我確實把它放在一個包裏,然後就這樣試了。我正在使用提供的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