Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions flyway-database-gaussdb/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-community-db-support</artifactId>
<version>10.24.0</version>
</parent>

<artifactId>flyway-database-gaussdb</artifactId>
<name>${project.artifactId}</name>

<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>flyway-core</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>

<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*-
* ========================LICENSE_START=================================
* flyway-database-ignite
* ========================================================================
* Copyright (C) 2010 - 2025 Red Gate Software Ltd
* ========================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* =========================LICENSE_END==================================
*/

package org.flywaydb.community.database;

import org.flywaydb.core.api.FlywayException;
import org.flywaydb.core.extensibility.PluginMetadata;
import org.flywaydb.core.internal.util.FileUtils;

import java.io.IOException;
import java.nio.charset.StandardCharsets;

public class GaussDBDatabaseExtension implements PluginMetadata {
public String getDescription() {
return "Community-contributed Ignite database support extension " + readVersion() + " by Redgate";
}

public static String readVersion() {
try {
return FileUtils.copyToString(
GaussDBDatabaseExtension.class.getClassLoader().getResourceAsStream("org/flywaydb/community/database/gaussdb/version.txt"),
StandardCharsets.UTF_8);
} catch (IOException e) {
throw new FlywayException("Unable to read extension version: " + e.getMessage(), e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
/*-
* ========================LICENSE_START=================================
* flyway-database-gaussdb
* ========================================================================
* Copyright (C) 2010 - 2025 Red Gate Software Ltd
* ========================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* =========================LICENSE_END==================================
*/
package org.flywaydb.community.database.gaussdb;

import lombok.CustomLog;
import org.flywaydb.core.api.FlywayException;
import org.flywaydb.core.api.configuration.Configuration;
import org.flywaydb.core.internal.exception.FlywaySqlException;
import org.flywaydb.core.internal.jdbc.JdbcTemplate;
import org.flywaydb.core.internal.jdbc.TransactionalExecutionTemplate;
import org.flywaydb.core.internal.strategy.RetryStrategy;
import org.flywaydb.core.internal.util.FlywayDbWebsiteLinks;
import org.flywaydb.core.internal.util.SqlCallable;

import java.sql.SQLException;
import java.util.List;
import java.util.concurrent.Callable;

/**
* @author chen zhida
*
* Notes: Original code of this class is based on PostgreSQLAdvisoryLockTemplate.
*/
@CustomLog
public class GaussDBAdvisoryLockTemplate {
private static final long LOCK_MAGIC_NUM =
(0x46L << 40) // F
+ (0x6CL << 32) // l
+ (0x79L << 24) // y
+ (0x77 << 16) // w
+ (0x61 << 8) // a
+ 0x79; // y

private final Configuration configuration;
private final JdbcTemplate jdbcTemplate;
private final long lockNum;

GaussDBAdvisoryLockTemplate(Configuration configuration, JdbcTemplate jdbcTemplate, int discriminator) {
this.configuration = configuration;
this.jdbcTemplate = jdbcTemplate;
this.lockNum = LOCK_MAGIC_NUM + discriminator;
}

public <T> T execute(Callable<T> callable) {
GaussDBConfigurationExtension configurationExtension = configuration.getPluginRegister().getPlugin(GaussDBConfigurationExtension.class);

if (configurationExtension.isTransactionalLock()) {
return new TransactionalExecutionTemplate(jdbcTemplate.getConnection(), true).execute(() -> execute(callable, this::tryLockTransactional));
} else {
RuntimeException rethrow = null;
try {
return execute(callable, this::tryLock);
} catch (RuntimeException e) {
rethrow = e;
throw rethrow;
} finally {
unlock(rethrow);
}
}
}

private <T> T execute(Callable<T> callable, SqlCallable<Boolean> tryLock) {
try {
lock(tryLock);
return callable.call();
} catch (SQLException e) {
throw new FlywaySqlException("Unable to acquire GaussDB advisory lock", e);
} catch (Exception e) {
if (e instanceof RuntimeException) {
throw (RuntimeException) e;
}
throw new FlywayException(e);
}
}

private void lock(SqlCallable<Boolean> tryLock) throws SQLException {
RetryStrategy strategy = new RetryStrategy();
strategy.doWithRetries(tryLock, "Interrupted while attempting to acquire GaussDB advisory lock",
"Number of retries exceeded while attempting to acquire GaussDB advisory lock. " +
"Configure the number of retries with the 'lockRetryCount' configuration option: " + FlywayDbWebsiteLinks.LOCK_RETRY_COUNT);
}

private boolean tryLockTransactional() throws SQLException {
List<Boolean> results = jdbcTemplate.query("SELECT pg_try_advisory_xact_lock(" + lockNum + ")", rs -> rs.getBoolean("pg_try_advisory_xact_lock"));
return results.size() == 1 && results.get(0);
}

private boolean tryLock() throws SQLException {
List<Boolean> results = jdbcTemplate.query("SELECT pg_try_advisory_lock(" + lockNum + ")", rs -> rs.getBoolean("pg_try_advisory_lock"));
return results.size() == 1 && results.get(0);
}

private void unlock(RuntimeException rethrow) throws FlywaySqlException {
try {
boolean unlocked = jdbcTemplate.queryForBoolean("SELECT pg_advisory_unlock(" + lockNum + ")");
if (!unlocked) {
if (rethrow == null) {
throw new FlywayException("Unable to release GaussDB advisory lock");
} else {
LOG.error("Unable to release GaussDB advisory lock");
}
}
} catch (SQLException e) {
if (rethrow == null) {
throw new FlywaySqlException("Unable to release GaussDB advisory lock", e);
} else {
LOG.error("Unable to release GaussDB advisory lock", e);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*-
* ========================LICENSE_START=================================
* flyway-database-gaussdb
* ========================================================================
* Copyright (C) 2010 - 2025 Red Gate Software Ltd
* ========================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* =========================LICENSE_END==================================
*/
package org.flywaydb.community.database.gaussdb;

import lombok.Data;
import org.flywaydb.core.extensibility.ConfigurationExtension;

/**
* @author chen zhida
*
* Notes: Original code of this class is based on PostgreSQLConfigurationExtension
*/
@Data
public class GaussDBConfigurationExtension implements ConfigurationExtension {
private static final String TRANSACTIONAL_LOCK = "flyway.gaussdb.transactional.lock";

private TransactionalModel transactional = null;

public boolean isTransactionalLock() {
// null is default, default is true, done this way for merge reasons.
return transactional == null || transactional.getLock() == null || transactional.getLock();
}

public void setTransactionalLock(boolean transactionalLock) {
transactional = new TransactionalModel();
transactional.setLock(transactionalLock);
}
@Override
public String getConfigurationParameterFromEnvironmentVariable(String environmentVariable) {
if ("FLYWAY_POSTGRESQL_TRANSACTIONAL_LOCK".equals(environmentVariable)) {
return TRANSACTIONAL_LOCK;
}
return null;
}

@Override
public String getNamespace() {
return "gaussdb";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/*-
* ========================LICENSE_START=================================
* flyway-database-gaussdb
* ========================================================================
* Copyright (C) 2010 - 2025 Red Gate Software Ltd
* ========================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* =========================LICENSE_END==================================
*/
package org.flywaydb.community.database.gaussdb;

import lombok.Getter;
import org.flywaydb.core.api.FlywayException;
import org.flywaydb.core.internal.database.base.Connection;
import org.flywaydb.core.internal.database.base.Schema;
import org.flywaydb.core.internal.database.base.Table;
import org.flywaydb.core.internal.exception.FlywaySqlException;
import org.flywaydb.core.internal.util.StringUtils;

import java.sql.SQLException;
import java.util.concurrent.Callable;

/**
* @author chen zhida
*
* Notes: Original code of this class is based on PostgreSQLConnection.
*/
public class GaussDBConnection extends Connection<GaussDBDatabase> {
private final String originalRole;
@Getter
private final boolean awsRds;

protected GaussDBConnection(GaussDBDatabase database, java.sql.Connection connection) {
super(database, connection);

try {
originalRole = jdbcTemplate.queryForString("SELECT CURRENT_USER");
} catch (SQLException e) {
throw new FlywaySqlException("Unable to determine current user", e);
}

awsRds = rdsAdminExists();
}

@Override
public Schema doGetCurrentSchema() throws SQLException {
String currentSchema = jdbcTemplate.queryForString("SELECT current_schema");
String searchPath = getCurrentSchemaNameOrSearchPath();

if (!StringUtils.hasText(currentSchema) && !StringUtils.hasText(searchPath)) {
throw new FlywayException("Unable to determine current schema as search_path is empty. " +
"Set the current schema in currentSchema parameter of the JDBC URL or in Flyway's schemas property.");
}

String schema = StringUtils.hasText(currentSchema) ? currentSchema : searchPath;

return getSchema(schema);
}

@Override
protected String getCurrentSchemaNameOrSearchPath() throws SQLException {
return jdbcTemplate.queryForString("SHOW search_path");
}

@Override
public void changeCurrentSchemaTo(Schema schema) {
try {
if (schema.getName().equals(originalSchemaNameOrSearchPath) || originalSchemaNameOrSearchPath.startsWith(schema.getName() + ",") || !schema.exists()) {
return;
}

if (StringUtils.hasText(originalSchemaNameOrSearchPath)) {
doChangeCurrentSchemaOrSearchPathTo(schema + "," + originalSchemaNameOrSearchPath);
} else {
doChangeCurrentSchemaOrSearchPathTo(schema.toString());
}
} catch (SQLException e) {
throw new FlywaySqlException("Error setting current schema to " + schema, e);
}
}

@Override
public void doChangeCurrentSchemaOrSearchPathTo(String schema) throws SQLException {
jdbcTemplate.execute("SELECT set_config('search_path', ?, false)", schema);
}

@Override
public Schema getSchema(String name) {
return new GaussDBSchema(jdbcTemplate, database, name);
}

@Override
public <T> T lock(Table table, Callable<T> callable) {
return new GaussDBAdvisoryLockTemplate(database.getConfiguration(), jdbcTemplate, table.toString().hashCode()).execute(callable);
}

private boolean rdsAdminExists() {
try {
return StringUtils.hasText(jdbcTemplate.queryForString("SELECT rolname FROM pg_roles WHERE rolname ILIKE 'rds_superuser';"));
} catch (Exception e) {
return false;
}
}
}
Loading