ENT-2530 Corda fails when running against PostgreSQL with uppercase schema namespace. (#1412)

Fix Liquibase quoting strategy for PostgreSQL to follow Corda convention.
In Liquibase for Postgres if schema name has uppercase or lowercase characters only then Liquibase would send it without double quotes and effectively make them lowercase.
Alter Postgres Liquibase dialect to wrap schema names in double quotes. This allows e.g. schemaName=ALICE be send as "ALICE" by Liquibase.
Corda persistence was already wrapping with double quotes, but certain cases in Liquibase were not following this. The result was that SQL run by Liquibase couldn't find schema (e.g. select ALICE.tablename ... in Postgres matched alice schema name not ALICE one).
The fix enables one integration test to be run now against standalone databases.
This commit is contained in:
szymonsztuka 2018-10-01 12:04:57 +01:00 committed by GitHub
parent 6404dc3391
commit f2c0beb8d0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 31 additions and 5 deletions

View File

@ -86,7 +86,7 @@ class HibernateConfiguration(
config.setProperty("hibernate.hbm2ddl.auto","validate")
databaseConfig.schema?.apply {
//preserving case-sensitive schema name for PostgreSQL by wrapping in double quotes, schema without double quotes would be treated as case-insensitive (lower cases)
// Enterprise only - preserving case-sensitive schema name for PostgreSQL by wrapping in double quotes, schema without double quotes would be treated as case-insensitive (lower cases)
val schemaName = if (jdbcUrl.contains(":postgresql:", ignoreCase = true) && !databaseConfig.schema.startsWith("\"")) {
"\"" + databaseConfig.schema + "\""
} else {
@ -231,4 +231,3 @@ class HibernateConfiguration(
/** Allow Oracle database drivers ojdbc7.jar and ojdbc8.jar to deserialize classes from oracle.sql.converter package. */
fun oracleJdbcDriverSerialFilter(clazz: Class<*>): Boolean = clazz.name.startsWith("oracle.sql.converter.")

View File

@ -17,6 +17,9 @@ import java.io.ByteArrayInputStream
import java.io.InputStream
import java.io.Writer
import javax.sql.DataSource
import liquibase.database.core.PostgresDatabase
import liquibase.structure.DatabaseObject
import liquibase.structure.core.Schema
class SchemaMigration(
val schemas: Set<MappedSchema>,
@ -135,6 +138,7 @@ class SchemaMigration(
private fun getLiquibaseDatabase(conn: JdbcConnection): Database {
// Enterprise only
// The standard MSSQLDatabase in Liquibase does not support sequences for Ms Azure.
// this class just overrides that behaviour
class AzureDatabase(conn: JdbcConnection) : MSSQLDatabase() {
@ -147,9 +151,33 @@ class SchemaMigration(
override fun supportsSequences(): Boolean = true
}
// Enterprise only
// Postgres - Wrap schema name into double quotes to preserve case sensitivity
class PostgresDatabaseFixed(conn: JdbcConnection) : PostgresDatabase() {
init {
this.connection = conn
}
override fun getPriority(): Int { return super.getPriority() + 1 }
// For PostgreSQL if a schema name has uppercase or lowercase characters only then Liquibase would add it to generated query
// without double quotes and effectively make them lowercase. This is inconsistent with Corda which wraps schema name for PostgreSQL in double quotes.
// The overridden method ensures Liquibase wraps schema name into double quotes.
override fun mustQuoteObjectName(objectName: String, objectType: Class<out DatabaseObject>?): Boolean {
return if (objectType == Schema::class.java)
true
else
super.mustQuoteObjectName(objectName, objectType)
}
}
val liquibaseDbImplementation = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(conn)
return if (liquibaseDbImplementation is MSSQLDatabase) AzureDatabase(conn) else liquibaseDbImplementation
return when (liquibaseDbImplementation) {
is PostgresDatabase -> PostgresDatabaseFixed(conn) // Enterprise only
is MSSQLDatabase -> AzureDatabase(conn) // Enterprise only
else -> liquibaseDbImplementation
}
}
/** For existing database created before verions 4.0 add Liquibase support - creates DATABASECHANGELOG and DATABASECHANGELOGLOCK tables and mark changesets are executed. */

View File

@ -58,7 +58,7 @@ class DriverTests : IntegrationTest() {
@ClassRule
@JvmField
val databaseSchemas = IntegrationTestSchemas(DUMMY_BANK_A_NAME.toDatabaseSchemaName(), DUMMY_NOTARY_NAME.toDatabaseSchemaName(),
DUMMY_REGULATOR_NAME.toDatabaseSchemaName(), BOB_NAME.toDatabaseSchemaName(), DUMMY_BANK_B_NAME.toDatabaseSchemaName())
DUMMY_REGULATOR_NAME.toDatabaseSchemaName(), BOB_NAME.toDatabaseSchemaName(), DUMMY_BANK_B_NAME.toDatabaseSchemaName(), "R3CEV")
}
@Test
@ -157,7 +157,6 @@ class DriverTests : IntegrationTest() {
@Test
fun `driver rejects multiple nodes with the same organisation name`() {
assumeTrue(!IntegrationTest.isRemoteDatabaseMode()) // Enterprise only - disable test, Liquibase doesn't quote schema name with all uppercase or lowercase letters
driver(DriverParameters(startNodesInProcess = true, notarySpecs = emptyList())) {
newNode(CordaX500Name(commonName = "Notary", organisation = "R3CEV", locality = "New York", country = "US"))().getOrThrow()
assertThatIllegalArgumentException().isThrownBy {